"@)
@@ -9695,7 +9813,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlScopeInsights.AppendLine(@'
- No Management Group Diagnostic settings docs
+ No Management Group Diagnostic settings learn
'@)
}
#endregion ScopeInsightsDiagnosticsMg
@@ -10070,7 +10188,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlScopeInsights.AppendLine(@"
CAF Naming Recommendation Compliance
-
CAF - Recommended abbreviations for Azure resource types docs
+
CAF - Recommended abbreviations for Azure resource types learn
Resource details can be found in the CSV output *_ResourcesAll.csv
Download CSV
semicolon |
comma
@@ -10621,7 +10739,7 @@ extensions: [{ name: 'sort' }]
UserAssigned Managed Identities assigned to Resources / vice versa
-
Managed identity 'user-assigned' vs 'system-assigned' docs
+
Managed identity 'user-assigned' vs 'system-assigned' learn
Download CSV
semicolon |
comma
@@ -12424,8 +12542,13 @@ function processStorageAccountAnalysis {
}
}
- $storageAccounts | ForEach-Object -Parallel {
- $storageAccount = $_
+ $batchSize = [math]::ceiling($storageAccounts.Count / $ThrottleLimit)
+ Write-Host "Optimal batch size: $($batchSize)"
+ $counterBatch = [PSCustomObject] @{ Value = 0 }
+ $storageAccountsBatch = ($storageAccounts) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ Write-Host "Processing data in $($storageAccountsBatch.Count) batches"
+
+ $storageAccountsBatch | ForEach-Object -Parallel {
$azAPICallConf = $using:azAPICallConf
$arrayStorageAccountAnalysisResults = $using:arrayStorageAccountAnalysisResults
$htAllSubscriptionsFromAPI = $using:htAllSubscriptionsFromAPI
@@ -12435,317 +12558,320 @@ function processStorageAccountAnalysis {
$htSACost = $using:htSACost
$StorageAccountAccessAnalysisSubscriptionTags = $using:StorageAccountAccessAnalysisSubscriptionTags
$StorageAccountAccessAnalysisStorageAccountTags = $using:StorageAccountAccessAnalysisStorageAccountTags
- $listContainersSuccess = 'n/a'
- $containersCount = 'n/a'
- $arrayContainers = @()
- $arrayContainersAnonymousContainer = @()
- $arrayContainersAnonymousBlob = @()
- $staticWebsitesState = 'n/a'
- $webSiteResponds = 'n/a'
- $subscriptionId = ($storageAccount.SA.id -split '/')[2]
- $resourceGroupName = ($storageAccount.SA.id -split '/')[4]
- $subDetails = $htAllSubscriptionsFromAPI.($subscriptionId).subDetails
- Write-Host "Processing Storage Account '$($storageAccount.SA.name)' - Subscription: '$($subDetails.displayName)' ($subscriptionId) [$($subDetails.subscriptionPolicies.quotaId)]"
+ foreach ($storageAccount in $_.Group) {
+ $listContainersSuccess = 'n/a'
+ $containersCount = 'n/a'
+ $arrayContainers = [System.Collections.ArrayList]@()
+ $arrayContainersAnonymousContainer = [System.Collections.ArrayList]@()
+ $arrayContainersAnonymousBlob = [System.Collections.ArrayList]@()
+ $staticWebsitesState = 'n/a'
+ $webSiteResponds = 'n/a'
- if ($storageAccount.SA.Properties.primaryEndpoints.blob) {
+ $subscriptionId = ($storageAccount.SA.id -split '/')[2]
+ $resourceGroupName = ($storageAccount.SA.id -split '/')[4]
+ $subDetails = $htAllSubscriptionsFromAPI.($subscriptionId).subDetails
- $urlServiceProps = "$($storageAccount.SA.Properties.primaryEndpoints.blob)?restype=service&comp=properties"
- $saProperties = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $urlServiceProps -method 'GET' -listenOn 'Content' -currentTask "$($storageAccount.SA.name) get restype=service&comp=properties" -saResourceGroupName $resourceGroupName -unhandledErrorAction Continue
- if ($saProperties) {
- if ($saProperties -eq 'AuthorizationFailure' -or $saProperties -eq 'AuthorizationPermissionDenied' -or $saProperties -eq 'ResourceUnavailable' -or $saProperties -eq 'AuthorizationPermissionMismatch' ) {
- if ($saProperties -eq 'ResourceUnavailable') {
- $staticWebsitesState = $saProperties
- }
- }
- else {
- try {
- # ? https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/218#issuecomment-1854516882
- if ($saProperties.gettype().Name -eq 'Byte[]') {
- $byteArray = [byte[]]$saProperties
- $saProperties = [System.Text.Encoding]::UTF8.GetString($byteArray)
- }
+ Write-Host "Processing Storage Account '$($storageAccount.SA.name)' - Subscription: '$($subDetails.displayName)' ($subscriptionId) [$($subDetails.subscriptionPolicies.quotaId)]"
+
+ if ($storageAccount.SA.Properties.primaryEndpoints.blob) {
- # $xmlSaProperties = [xml]([string]$saProperties -replace $saProperties.Substring(0, 3)) # Leading character:  (PS version <= 7.3.9)
- # $xmlSaProperties = [xml]([string]$saProperties -replace $saProperties.Substring(0, 1)) # Leading character: or U+feff (PS version >= 7.4.0)
- $xmlSaProperties = [xml]($saProperties -replace '^.*?<', '<') # Universal fix for all PS versions
- if ($xmlSaProperties.StorageServiceProperties.StaticWebsite) {
- if ($xmlSaProperties.StorageServiceProperties.StaticWebsite.Enabled -eq $true) {
- $staticWebsitesState = $true
+ $urlServiceProps = "$($storageAccount.SA.Properties.primaryEndpoints.blob)?restype=service&comp=properties"
+ $saProperties = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $urlServiceProps -method 'GET' -listenOn 'Content' -currentTask "$($storageAccount.SA.name) get restype=service&comp=properties" -saResourceGroupName $resourceGroupName -unhandledErrorAction Continue
+ if ($saProperties) {
+ if ($saProperties -eq 'AuthorizationFailure' -or $saProperties -eq 'AuthorizationPermissionDenied' -or $saProperties -eq 'ResourceUnavailable' -or $saProperties -eq 'AuthorizationPermissionMismatch' ) {
+ if ($saProperties -eq 'ResourceUnavailable') {
+ $staticWebsitesState = $saProperties
+ }
+ }
+ else {
+ try {
+ # ? https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/218#issuecomment-1854516882
+ if ($saProperties.gettype().Name -eq 'Byte[]') {
+ $byteArray = [byte[]]$saProperties
+ $saProperties = [System.Text.Encoding]::UTF8.GetString($byteArray)
}
- else {
- $staticWebsitesState = $false
+
+ # $xmlSaProperties = [xml]([string]$saProperties -replace $saProperties.Substring(0, 3)) # Leading character:  (PS version <= 7.3.9)
+ # $xmlSaProperties = [xml]([string]$saProperties -replace $saProperties.Substring(0, 1)) # Leading character: or U+feff (PS version >= 7.4.0)
+ $xmlSaProperties = [xml]($saProperties -replace '^.*?<', '<') # Universal fix for all PS versions
+ if ($xmlSaProperties.StorageServiceProperties.StaticWebsite) {
+ if ($xmlSaProperties.StorageServiceProperties.StaticWebsite.Enabled -eq $true) {
+ $staticWebsitesState = $true
+ }
+ else {
+ $staticWebsitesState = $false
+ }
}
}
- }
- catch {
- Write-Host "XMLSAPropertiesFailed: Subscription: $($subDetails.displayName) ($subscriptionId) - Storage Account: $($storageAccount.SA.name)"
- Write-Host $($saProperties.ForEach({ [char]$_ }) -join '') -ForegroundColor Cyan
+ catch {
+ Write-Host "XMLSAPropertiesFailed: Subscription: $($subDetails.displayName) ($subscriptionId) - Storage Account: $($storageAccount.SA.name)"
+ Write-Host $($saProperties.ForEach({ [char]$_ }) -join '') -ForegroundColor Cyan
+ }
}
}
- }
- $urlCompList = "$($storageAccount.SA.Properties.primaryEndpoints.blob)?comp=list"
- $listContainers = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $urlCompList -method 'GET' -listenOn 'Content' -currentTask "$($storageAccount.SA.name) get comp=list" -unhandledErrorAction Continue
- if ($listContainers) {
- if ($listContainers -eq 'AuthorizationFailure' -or $listContainers -eq 'AuthorizationPermissionDenied' -or $listContainers -eq 'ResourceUnavailable' -or $listContainers -eq 'AuthorizationPermissionMismatch') {
- if ($listContainers -eq 'ResourceUnavailable') {
- $listContainersSuccess = $listContainers
+ $urlCompList = "$($storageAccount.SA.Properties.primaryEndpoints.blob)?comp=list"
+ $listContainers = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $urlCompList -method 'GET' -listenOn 'Content' -currentTask "$($storageAccount.SA.name) get comp=list" -unhandledErrorAction Continue
+ if ($listContainers) {
+ if ($listContainers -eq 'AuthorizationFailure' -or $listContainers -eq 'AuthorizationPermissionDenied' -or $listContainers -eq 'ResourceUnavailable' -or $listContainers -eq 'AuthorizationPermissionMismatch') {
+ if ($listContainers -eq 'ResourceUnavailable') {
+ $listContainersSuccess = $listContainers
+ }
+ else {
+ $listContainersSuccess = $false
+ }
}
else {
- $listContainersSuccess = $false
- }
- }
- else {
- $listContainersSuccess = $true
- }
-
- if ($listContainersSuccess -eq $true) {
- # ? https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/218#issuecomment-1854516882
- if ($listContainers.gettype().Name -eq 'Byte[]') {
- $byteArray = [byte[]]$listContainers
- $listContainers = [System.Text.Encoding]::UTF8.GetString($byteArray)
+ $listContainersSuccess = $true
}
- # $xmlListContainers = [xml]([string]$listContainers -replace $listContainers.Substring(0, 3)) # Leading character:  (PS version <= 7.3.9)
- # $xmlListContainers = [xml]([string]$listContainers -replace $listContainers.Substring(0, 1)) # Leading character: or U+feff (PS version >= 7.4.0)
- $xmlListContainers = [xml]($listContainers -replace '^.*?<', '<') # Universal fix for all PS versions
- $containersCount = $xmlListContainers.EnumerationResults.Containers.Container.Count
+ if ($listContainersSuccess -eq $true) {
+ # ? https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/218#issuecomment-1854516882
+ if ($listContainers.gettype().Name -eq 'Byte[]') {
+ $byteArray = [byte[]]$listContainers
+ $listContainers = [System.Text.Encoding]::UTF8.GetString($byteArray)
+ }
- foreach ($container in $xmlListContainers.EnumerationResults.Containers.Container) {
- $arrayContainers += $container.Name
- if ($container.Name -eq '$web' -and $staticWebsitesState) {
- if ($storageAccount.SA.properties.primaryEndpoints.web) {
- try {
- $testStaticWebsiteResponse = Invoke-WebRequest -Uri $storageAccount.SA.properties.primaryEndpoints.web -Method 'HEAD'
- $webSiteResponds = $true
- }
- catch {
- $webSiteResponds = $false
+ # $xmlListContainers = [xml]([string]$listContainers -replace $listContainers.Substring(0, 3)) # Leading character:  (PS version <= 7.3.9)
+ # $xmlListContainers = [xml]([string]$listContainers -replace $listContainers.Substring(0, 1)) # Leading character: or U+feff (PS version >= 7.4.0)
+ $xmlListContainers = [xml]($listContainers -replace '^.*?<', '<') # Universal fix for all PS versions
+ $containersCount = $xmlListContainers.EnumerationResults.Containers.Container.Count
+
+ foreach ($container in $xmlListContainers.EnumerationResults.Containers.Container) {
+ $null = $arrayContainers.Add($container.Name)
+ if ($container.Name -eq '$web' -and $staticWebsitesState) {
+ if ($storageAccount.SA.properties.primaryEndpoints.web) {
+ try {
+ $testStaticWebsiteResponse = Invoke-WebRequest -Uri $storageAccount.SA.properties.primaryEndpoints.web -Method 'HEAD'
+ $webSiteResponds = $true
+ }
+ catch {
+ $webSiteResponds = $false
+ }
}
}
- }
- if ($container.Properties.PublicAccess) {
- if ($container.Properties.PublicAccess -eq 'blob') {
- $arrayContainersAnonymousBlob += $container.Name
- }
- if ($container.Properties.PublicAccess -eq 'container') {
- $arrayContainersAnonymousContainer += $container.Name
+ if ($container.Properties.PublicAccess) {
+ if ($container.Properties.PublicAccess -eq 'blob') {
+ $null = $arrayContainersAnonymousBlob.Add($container.Name)
+ }
+ if ($container.Properties.PublicAccess -eq 'container') {
+ $null = $arrayContainersAnonymousContainer.Add($container.Name)
+ }
}
}
}
}
}
- }
- $allowSharedKeyAccess = $storageAccount.SA.properties.allowSharedKeyAccess
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowSharedKeyAccess)) {
- $allowSharedKeyAccess = 'likely True'
- }
- $requireInfrastructureEncryption = $storageAccount.SA.properties.encryption.requireInfrastructureEncryption
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.encryption.requireInfrastructureEncryption)) {
- $requireInfrastructureEncryption = 'likely False'
- }
+ $allowSharedKeyAccess = $storageAccount.SA.properties.allowSharedKeyAccess
+ if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowSharedKeyAccess)) {
+ $allowSharedKeyAccess = 'likely True'
+ }
+ $requireInfrastructureEncryption = $storageAccount.SA.properties.encryption.requireInfrastructureEncryption
+ if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.encryption.requireInfrastructureEncryption)) {
+ $requireInfrastructureEncryption = 'likely False'
+ }
- $arrayResourceAccessRules = [System.Collections.ArrayList]@()
- if ($storageAccount.SA.properties.networkAcls.resourceAccessRules) {
- if ($storageAccount.SA.properties.networkAcls.resourceAccessRules.count -gt 0) {
- foreach ($resourceAccessRule in $storageAccount.SA.properties.networkAcls.resourceAccessRules) {
+ $arrayResourceAccessRules = [System.Collections.ArrayList]@()
+ if ($storageAccount.SA.properties.networkAcls.resourceAccessRules) {
+ if ($storageAccount.SA.properties.networkAcls.resourceAccessRules.count -gt 0) {
+ foreach ($resourceAccessRule in $storageAccount.SA.properties.networkAcls.resourceAccessRules) {
- $resourceAccessRuleResourceIdSplitted = $resourceAccessRule.resourceId -split '/'
- $resourceType = "$($resourceAccessRuleResourceIdSplitted[6])/$($resourceAccessRuleResourceIdSplitted[7])"
+ $resourceAccessRuleResourceIdSplitted = $resourceAccessRule.resourceId -split '/'
+ $resourceType = "$($resourceAccessRuleResourceIdSplitted[6])/$($resourceAccessRuleResourceIdSplitted[7])"
- [regex]$regex = '\*+'
- #$resourceAccessRule.resourceId
- switch ($regex.matches($resourceAccessRule.resourceId).count) {
- { $_ -eq 1 } {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'resourceGroup'
- sort = 3
- })
- }
- { $_ -eq 2 } {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'subscription'
- sort = 2
- })
- }
- { $_ -eq 3 } {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'tenant'
- sort = 1
- })
- }
- default {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'resource'
- resource = $resourceAccessRule.resourceId
- sort = 0
- })
+ [regex]$regex = '\*+'
+ #$resourceAccessRule.resourceId
+ switch ($regex.matches($resourceAccessRule.resourceId).count) {
+ { $_ -eq 1 } {
+ $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
+ resourcetype = $resourceType
+ range = 'resourceGroup'
+ sort = 3
+ })
+ }
+ { $_ -eq 2 } {
+ $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
+ resourcetype = $resourceType
+ range = 'subscription'
+ sort = 2
+ })
+ }
+ { $_ -eq 3 } {
+ $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
+ resourcetype = $resourceType
+ range = 'tenant'
+ sort = 1
+ })
+ }
+ default {
+ $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
+ resourcetype = $resourceType
+ range = 'resource'
+ resource = $resourceAccessRule.resourceId
+ sort = 0
+ })
+ }
}
}
}
}
- }
- $resourceAccessRulesCount = $arrayResourceAccessRules.count
- if ($resourceAccessRulesCount -eq 0) {
- $resourceAccessRules = ''
- }
- else {
- $ht = @{}
- foreach ($accessRulePerRange in $arrayResourceAccessRules | Group-Object -Property range | Sort-Object -Property Name -Descending) {
+ $resourceAccessRulesCount = $arrayResourceAccessRules.count
+ if ($resourceAccessRulesCount -eq 0) {
+ $resourceAccessRules = ''
+ }
+ else {
+ $ht = @{}
+ foreach ($accessRulePerRange in $arrayResourceAccessRules | Group-Object -Property range | Sort-Object -Property Name -Descending) {
- if ($accessRulePerRange.Name -eq 'resource') {
- $arrayResources = @()
- foreach ($resource in $accessRulePerRange.Group.resource | Sort-Object) {
- $arrayResources += $resource
+ if ($accessRulePerRange.Name -eq 'resource') {
+ $arrayResources = [System.Collections.ArrayList]@()
+ foreach ($resource in $accessRulePerRange.Group.resource | Sort-Object) {
+ $null = $arrayResources.Add($resource)
+ }
+ $ht.($accessRulePerRange.Name) = ($arrayResources)
}
- $ht.($accessRulePerRange.Name) = [array]($arrayResources)
- }
- else {
- $arrayResourceTypes = @()
- foreach ($resourceType in $accessRulePerRange.Group.resourceType | Sort-Object) {
- $arrayResourceTypes += $resourceType
+ else {
+ $arrayResourceTypes = [System.Collections.ArrayList]@()
+ foreach ($resourceType in $accessRulePerRange.Group.resourceType | Sort-Object) {
+ $null = $arrayResourceTypes.Add($resourceType)
+ }
+ $ht.($accessRulePerRange.Name) = ($arrayResourceTypes)
}
- $ht.($accessRulePerRange.Name) = [array]($arrayResourceTypes)
}
+ $resourceAccessRules = $ht | ConvertTo-Json
}
- $resourceAccessRules = $ht | ConvertTo-Json
- }
-
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.publicNetworkAccess)) {
- $publicNetworkAccess = 'likely Enabled'
- }
- else {
- $publicNetworkAccess = $storageAccount.SA.properties.publicNetworkAccess
- }
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowedCopyScope)) {
- $allowedCopyScope = 'From any Storage Account'
- }
- else {
- $allowedCopyScope = $storageAccount.SA.properties.allowedCopyScope
- }
+ if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.publicNetworkAccess)) {
+ $publicNetworkAccess = 'likely Enabled'
+ }
+ else {
+ $publicNetworkAccess = $storageAccount.SA.properties.publicNetworkAccess
+ }
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowCrossTenantReplication)) {
- if ($allowedCopyScope -ne 'From any Storage Account') {
- $allowCrossTenantReplication = "likely False (allowedCopyScope=$allowedCopyScope)"
+ if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowedCopyScope)) {
+ $allowedCopyScope = 'From any Storage Account'
}
else {
- $allowCrossTenantReplication = 'likely True'
+ $allowedCopyScope = $storageAccount.SA.properties.allowedCopyScope
}
- }
- else {
- $allowCrossTenantReplication = $storageAccount.SA.properties.allowCrossTenantReplication
- }
- if ($storageAccount.SA.properties.dnsEndpointType) {
- $dnsEndpointType = $storageAccount.SA.properties.dnsEndpointType
- }
- else {
- $dnsEndpointType = 'standard'
- }
+ if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowCrossTenantReplication)) {
+ if ($allowedCopyScope -ne 'From any Storage Account') {
+ $allowCrossTenantReplication = "likely False (allowedCopyScope=$allowedCopyScope)"
+ }
+ else {
+ $allowCrossTenantReplication = 'likely True'
+ }
+ }
+ else {
+ $allowCrossTenantReplication = $storageAccount.SA.properties.allowCrossTenantReplication
+ }
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- if ($htSACost.($storageAccount.SA.id)) {
- $hlpCost = $htSACost.($storageAccount.SA.id)
- $saCost = $hlpCost.costAll
- $saCostCurrency = $hlpCost.currencyAll
- $saCostMeterCategories = $hlpCost.meterCategoryAll
+ if ($storageAccount.SA.properties.dnsEndpointType) {
+ $dnsEndpointType = $storageAccount.SA.properties.dnsEndpointType
}
else {
- $saCost = 'n/a'
- $saCostCurrency = 'n/a'
- $saCostMeterCategories = 'n/a'
+ $dnsEndpointType = 'standard'
}
- }
- else {
- $saCost = ''
- $saCostCurrency = ''
- $saCostMeterCategories = ''
- }
-
- $temp = [System.Collections.ArrayList]@()
- $null = $temp.Add([PSCustomObject]@{
- storageAccount = $storageAccount.SA.name
- kind = $storageAccount.SA.kind
- skuName = $storageAccount.SA.sku.name
- skuTier = $storageAccount.SA.sku.tier
- location = $storageAccount.SA.location
- creationTime = $storageAccount.SA.properties.creationTime
- allowBlobPublicAccess = $storageAccount.SA.properties.allowBlobPublicAccess
- publicNetworkAccess = $publicNetworkAccess
- SubscriptionId = $subscriptionId
- SubscriptionName = $subDetails.displayName
- subscriptionQuotaId = $subDetails.subscriptionPolicies.quotaId
- subscriptionMGPath = $htSubscriptionsMgPath.($subscriptionId).path -join '/'
- resourceGroup = $resourceGroupName
- networkAclsdefaultAction = $storageAccount.SA.properties.networkAcls.defaultAction
- staticWebsitesState = $staticWebsitesState
- staticWebsitesResponse = $webSiteResponds
- containersCanBeListed = $listContainersSuccess
- containersCount = $containersCount
- containers = $arrayContainers -join "$CSVDelimiterOpposite "
- containersAnonymousContainerCount = $arrayContainersAnonymousContainer.Count
- containersAnonymousContainer = $arrayContainersAnonymousContainer -join "$CSVDelimiterOpposite "
- containersAnonymousBlobCount = $arrayContainersAnonymousBlob.Count
- containersAnonymousBlob = $arrayContainersAnonymousBlob -join "$CSVDelimiterOpposite "
- ipRulesCount = $storageAccount.SA.properties.networkAcls.ipRules.Count
- ipRulesIPAddressList = ($storageAccount.SA.properties.networkAcls.ipRules.value | Sort-Object) -join "$CSVDelimiterOpposite "
- virtualNetworkRulesCount = $storageAccount.SA.properties.networkAcls.virtualNetworkRules.Count
- virtualNetworkRulesList = ($storageAccount.SA.properties.networkAcls.virtualNetworkRules.Id | Sort-Object) -join "$CSVDelimiterOpposite "
- resourceAccessRulesCount = $resourceAccessRulesCount
- resourceAccessRules = $resourceAccessRules
- bypass = ($storageAccount.SA.properties.networkAcls.bypass | Sort-Object) -join "$CSVDelimiterOpposite "
- supportsHttpsTrafficOnly = $storageAccount.SA.properties.supportsHttpsTrafficOnly
- minimumTlsVersion = $storageAccount.SA.properties.minimumTlsVersion
- allowSharedKeyAccess = $allowSharedKeyAccess
- requireInfrastructureEncryption = $requireInfrastructureEncryption
- allowedCopyScope = $allowedCopyScope
- allowCrossTenantReplication = $allowCrossTenantReplication
- dnsEndpointType = $dnsEndpointType
- usedCapacity = $storageAccount.SAUsedCapacity
- cost = $saCost
- metercategory = $saCostMeterCategories
- curreny = $saCostCurrency
- })
- if ($StorageAccountAccessAnalysisSubscriptionTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisSubscriptionTags.Count -gt 0) {
- foreach ($subTag4StorageAccountAccessAnalysis in $StorageAccountAccessAnalysisSubscriptionTags) {
- if ($htSubscriptionTags.($subscriptionId).$subTag4StorageAccountAccessAnalysis) {
- $temp | Add-Member -NotePropertyName "SubTag_$subTag4StorageAccountAccessAnalysis" -NotePropertyValue $($htSubscriptionTags.($subscriptionId).$subTag4StorageAccountAccessAnalysis)
+ if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
+ if ($htSACost.($storageAccount.SA.id)) {
+ $hlpCost = $htSACost.($storageAccount.SA.id)
+ $saCost = $hlpCost.costAll
+ $saCostCurrency = $hlpCost.currencyAll
+ $saCostMeterCategories = $hlpCost.meterCategoryAll
}
else {
- $temp | Add-Member -NotePropertyName "SubTag_$subTag4StorageAccountAccessAnalysis" -NotePropertyValue 'n/a'
+ $saCost = 'n/a'
+ $saCostCurrency = 'n/a'
+ $saCostMeterCategories = 'n/a'
}
}
- }
+ else {
+ $saCost = ''
+ $saCostCurrency = ''
+ $saCostMeterCategories = ''
+ }
+
+ $temp = [System.Collections.ArrayList]@()
+ $null = $temp.Add([PSCustomObject]@{
+ storageAccount = $storageAccount.SA.name
+ kind = $storageAccount.SA.kind
+ skuName = $storageAccount.SA.sku.name
+ skuTier = $storageAccount.SA.sku.tier
+ location = $storageAccount.SA.location
+ creationTime = $storageAccount.SA.properties.creationTime
+ allowBlobPublicAccess = $storageAccount.SA.properties.allowBlobPublicAccess
+ publicNetworkAccess = $publicNetworkAccess
+ SubscriptionId = $subscriptionId
+ SubscriptionName = $subDetails.displayName
+ subscriptionQuotaId = $subDetails.subscriptionPolicies.quotaId
+ subscriptionMGPath = $htSubscriptionsMgPath.($subscriptionId).path -join '/'
+ resourceGroup = $resourceGroupName
+ networkAclsdefaultAction = $storageAccount.SA.properties.networkAcls.defaultAction
+ staticWebsitesState = $staticWebsitesState
+ staticWebsitesResponse = $webSiteResponds
+ containersCanBeListed = $listContainersSuccess
+ containersCount = $containersCount
+ containers = $arrayContainers -join "$CSVDelimiterOpposite "
+ containersAnonymousContainerCount = $arrayContainersAnonymousContainer.Count
+ containersAnonymousContainer = $arrayContainersAnonymousContainer -join "$CSVDelimiterOpposite "
+ containersAnonymousBlobCount = $arrayContainersAnonymousBlob.Count
+ containersAnonymousBlob = $arrayContainersAnonymousBlob -join "$CSVDelimiterOpposite "
+ ipRulesCount = $storageAccount.SA.properties.networkAcls.ipRules.Count
+ ipRulesIPAddressList = ($storageAccount.SA.properties.networkAcls.ipRules.value | Sort-Object) -join "$CSVDelimiterOpposite "
+ virtualNetworkRulesCount = $storageAccount.SA.properties.networkAcls.virtualNetworkRules.Count
+ virtualNetworkRulesList = ($storageAccount.SA.properties.networkAcls.virtualNetworkRules.Id | Sort-Object) -join "$CSVDelimiterOpposite "
+ resourceAccessRulesCount = $resourceAccessRulesCount
+ resourceAccessRules = $resourceAccessRules
+ bypass = ($storageAccount.SA.properties.networkAcls.bypass | Sort-Object) -join "$CSVDelimiterOpposite "
+ supportsHttpsTrafficOnly = $storageAccount.SA.properties.supportsHttpsTrafficOnly
+ minimumTlsVersion = $storageAccount.SA.properties.minimumTlsVersion
+ allowSharedKeyAccess = $allowSharedKeyAccess
+ requireInfrastructureEncryption = $requireInfrastructureEncryption
+ allowedCopyScope = $allowedCopyScope
+ allowCrossTenantReplication = $allowCrossTenantReplication
+ dnsEndpointType = $dnsEndpointType
+ usedCapacity = $storageAccount.SAUsedCapacity
+ cost = $saCost
+ metercategory = $saCostMeterCategories
+ curreny = $saCostCurrency
+ })
- if ($StorageAccountAccessAnalysisStorageAccountTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisStorageAccountTags.Count -gt 0) {
- if ($storageAccount.SA.tags) {
- $htAllSATags = @{}
- foreach ($saTagName in ($storageAccount.SA.tags | Get-Member).where({ $_.MemberType -eq 'NoteProperty' }).Name) {
- $htAllSATags.$saTagName = $storageAccount.SA.tags.$saTagName
+ if ($StorageAccountAccessAnalysisSubscriptionTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisSubscriptionTags.Count -gt 0) {
+ foreach ($subTag4StorageAccountAccessAnalysis in $StorageAccountAccessAnalysisSubscriptionTags) {
+ if ($htSubscriptionTags.($subscriptionId).$subTag4StorageAccountAccessAnalysis) {
+ $temp | Add-Member -NotePropertyName "SubTag_$subTag4StorageAccountAccessAnalysis" -NotePropertyValue $($htSubscriptionTags.($subscriptionId).$subTag4StorageAccountAccessAnalysis)
+ }
+ else {
+ $temp | Add-Member -NotePropertyName "SubTag_$subTag4StorageAccountAccessAnalysis" -NotePropertyValue 'n/a'
+ }
}
}
- foreach ($saTag4StorageAccountAccessAnalysis in $StorageAccountAccessAnalysisStorageAccountTags) {
- if ($htAllSATags.$saTag4StorageAccountAccessAnalysis) {
- $temp | Add-Member -NotePropertyName "SATag_$saTag4StorageAccountAccessAnalysis" -NotePropertyValue $($htAllSATags.$saTag4StorageAccountAccessAnalysis)
+
+ if ($StorageAccountAccessAnalysisStorageAccountTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisStorageAccountTags.Count -gt 0) {
+ if ($storageAccount.SA.tags) {
+ $htAllSATags = @{}
+ foreach ($saTagName in ($storageAccount.SA.tags | Get-Member).where({ $_.MemberType -eq 'NoteProperty' }).Name) {
+ $htAllSATags.$saTagName = $storageAccount.SA.tags.$saTagName
+ }
}
- else {
- $temp | Add-Member -NotePropertyName "SATag_$saTag4StorageAccountAccessAnalysis" -NotePropertyValue 'n/a'
+ foreach ($saTag4StorageAccountAccessAnalysis in $StorageAccountAccessAnalysisStorageAccountTags) {
+ if ($htAllSATags.$saTag4StorageAccountAccessAnalysis) {
+ $temp | Add-Member -NotePropertyName "SATag_$saTag4StorageAccountAccessAnalysis" -NotePropertyValue $($htAllSATags.$saTag4StorageAccountAccessAnalysis)
+ }
+ else {
+ $temp | Add-Member -NotePropertyName "SATag_$saTag4StorageAccountAccessAnalysis" -NotePropertyValue 'n/a'
+ }
}
}
- }
-
- $null = $script:arrayStorageAccountAnalysisResults.AddRange($temp)
+ $null = $script:arrayStorageAccountAnalysisResults.AddRange($temp)
+ }
} -ThrottleLimit $ThrottleLimit
}
else {
@@ -15886,8 +16012,8 @@ extensions: [{ name: 'sort' }]
$policyType = 'unknown'
$policy = 'unknown'
- $arrayExemptedPolicies = @()
- $arrayExemptedPoliciesCSV = @()
+ $arrayExemptedPolicies = [System.Collections.ArrayList]@()
+ $arrayExemptedPoliciesCSV = [System.Collections.ArrayList]@()
$policiesExempted = $null
$policiesExemptedCSV = $null
$policiesExemptedCSVCount = $null
@@ -15939,8 +16065,8 @@ extensions: [{ name: 'sort' }]
}
}
- $arrayExemptedPolicies += $policyExempted
- $arrayExemptedPoliciesCSV += $policyExemptedCSV
+ $null = $arrayExemptedPolicies.Add($policyExempted)
+ $null = $arrayExemptedPoliciesCSV.Add($policyExemptedCSV)
}
$policiesExempted = "$($arrayExemptedPolicies.Count)/$($policiesTotalCount) ( $(($arrayExemptedPolicies | Sort-Object) -join ' '))"
@@ -16198,7 +16324,9 @@ extensions: [{ name: 'sort' }]
#this
if (-not $htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower())) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()) = @{}
+ $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()) = @{
+ roleassignments = [System.Collections.ArrayList]@()
+ }
}
if (($htCacheDefinitionsRole).($roleAssignment.RoleDefinitionId).IsCustom) {
@@ -16217,12 +16345,15 @@ extensions: [{ name: 'sort' }]
})
#this
- if ($htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments += $array
- }
- else {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = $array
- }
+ # if ($htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments) {
+ # $null = $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
+ # }
+ # else {
+ # #$script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = $array
+ # $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = [System.Collections.ArrayList]@()
+ # $null = $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
+ # }
+ $null = $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
}
}
@@ -16235,7 +16366,9 @@ extensions: [{ name: 'sort' }]
#this
if (-not $htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower())) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()) = @{}
+ $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()) = @{
+ roleassignments = [System.Collections.ArrayList]@()
+ }
}
if (($htCacheDefinitionsRole).($roleAssignment.RoleDefinitionId).IsCustom) {
@@ -16254,12 +16387,15 @@ extensions: [{ name: 'sort' }]
})
#this
- if ($htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments += $array
- }
- else {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = $array
- }
+ # if ($htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments) {
+ # $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
+ # }
+ # else {
+ # #$script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = $array
+ # $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = [System.Collections.ArrayList]@()
+ # $null = $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
+ # }
+ $null = $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
}
}
}
@@ -16269,61 +16405,81 @@ extensions: [{ name: 'sort' }]
#endregion PolicyAssignmentsRoleAssignmentMapping
#region PolicyAssignmentsUniqueRelations
- $startPolicyAssignmnetsUniqueRelations = Get-Date
- Write-Host ' processing PolicyAssignmnetsUniqueRelations'
+ $startPolicyAssignmentsUniqueRelations = Get-Date
+ Write-Host ' processing PolicyAssignmentsUniqueRelations'
$htPolicyAssignmentRelatedRoleAssignments = @{}
$htPolicyAssignmentRelatedExemptions = @{}
foreach ($policyAssignmentIdUnique in $policyBaseQueryUniqueAssignments) {
#region relatedRoleAssignments
- $relatedRoleAssignmentsArray = @()
- $relatedRoleAssignmentsArrayClear = @()
+ $relatedRoleAssignmentsArray = [System.Collections.ArrayList]@()
+ $relatedRoleAssignmentsArrayClear = [System.Collections.ArrayList]@()
if ($htPolicyAssignmentRoleAssignmentMappingCount -gt 0) {
- if ($htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId)) {
- foreach ($entry in $htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId).roleassignments) {
+ $policyAssignmentMapping = $htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId)
+ if ($null -ne $policyAssignmentMapping) {
+ foreach ($entry in $policyAssignmentMapping.roleassignments) {
if ($entry.roleDefinitionType -eq 'builtin') {
- $relatedRoleAssignmentsArray += "$($entry.roleDefinitionName) ($($entry.roleAssignmentId))"
+ $null = $relatedRoleAssignmentsArray.Add("$($entry.roleDefinitionName) ($($entry.roleAssignmentId))")
}
else {
- $relatedRoleAssignmentsArray += "$($entry.roleDefinitionName -replace '<', '<' -replace '>', '>') ($($entry.roleAssignmentId))"
+ $null = $relatedRoleAssignmentsArray.Add("$($entry.roleDefinitionName -replace '<', '<' -replace '>', '>') ($($entry.roleAssignmentId))")
}
- $relatedRoleAssignmentsArrayClear += "$($entry.roleDefinitionName) ($($entry.roleAssignmentId))"
+ $null = $relatedRoleAssignmentsArrayClear.Add("$($entry.roleDefinitionName) ($($entry.roleAssignmentId))")
}
}
}
+ # if ($htPolicyAssignmentRoleAssignmentMappingCount -gt 0) {
+ # if ($htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId)) {
+ # foreach ($entry in $htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId).roleassignments) {
+ # if ($entry.roleDefinitionType -eq 'builtin') {
+ # $null = $relatedRoleAssignmentsArray.Add("$($entry.roleDefinitionName) ($($entry.roleAssignmentId))")
+ # }
+ # else {
+ # $null = $relatedRoleAssignmentsArray.Add("$($entry.roleDefinitionName -replace '<', '<' -replace '>', '>') ($($entry.roleAssignmentId))")
+ # }
+ # $null = $relatedRoleAssignmentsArrayClear.Add("$($entry.roleDefinitionName) ($($entry.roleAssignmentId))")
+ # }
+ # }
+ # }
+
$htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId) = @{}
if (($relatedRoleAssignmentsArray).count -gt 0) {
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignments = ($relatedRoleAssignmentsArray | Sort-Object) -join "$CsvDelimiterOpposite "
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignmentsClear = ($relatedRoleAssignmentsArrayClear | Sort-Object) -join "$CsvDelimiterOpposite "
+ # $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignments = ($relatedRoleAssignmentsArray | Sort-Object) -join "$CsvDelimiterOpposite "
+ # $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignmentsClear = ($relatedRoleAssignmentsArrayClear | Sort-Object) -join "$CsvDelimiterOpposite "
+ $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId) = @{
+ relatedRoleAssignments = ($relatedRoleAssignmentsArray | Sort-Object) -join "$CsvDelimiterOpposite "
+ relatedRoleAssignmentsClear = ($relatedRoleAssignmentsArrayClear | Sort-Object) -join "$CsvDelimiterOpposite "
+ }
}
else {
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignments = 'none'
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignmentsClear = 'none'
+ # $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignments = 'none'
+ # $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignmentsClear = 'none'
+ $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId) = @{
+ relatedRoleAssignments = 'none'
+ relatedRoleAssignmentsClear = 'none'
+ }
}
#endregion relatedRoleAssignments
#region exemptions
- $arrayExemptions = @()
+ $arrayExemptions = [System.Collections.ArrayList]@()
foreach ($exemptionId in $htPolicyAssignmentExemptions.keys) {
if ($htPolicyAssignmentExemptions.($exemptionId).exemption.properties.policyAssignmentId -eq $policyAssignmentIdUnique.PolicyAssignmentId) {
- $arrayExemptions += $htPolicyAssignmentExemptions.($exemptionId).exemption
- if (-not $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId)) {
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId) = @{}
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptionsCount = 1
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptions = $arrayExemptions
- }
- else {
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptionsCount += 1
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptions = $arrayExemptions
- }
+ $null = $arrayExemptions.Add($htPolicyAssignmentExemptions.($exemptionId).exemption)
+ }
+ }
+ if ($arrayExemptions.Count -gt 0) {
+ $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId) = @{
+ exemptionsCount = $arrayExemptions.Count
+ exemptions = $arrayExemptions
}
}
#endregion exemptions
}
- $endPolicyAssignmnetsUniqueRelations = Get-Date
- Write-Host " PolicyAssignmnetsUniqueRelations processing duration: $((New-TimeSpan -Start $startPolicyAssignmnetsUniqueRelations -End $endPolicyAssignmnetsUniqueRelations).TotalMinutes) minutes ($((New-TimeSpan -Start $startPolicyAssignmnetsUniqueRelations -End $endPolicyAssignmnetsUniqueRelations).TotalSeconds) seconds)"
+ $endPolicyAssignmentsUniqueRelations = Get-Date
+ Write-Host " PolicyAssignmentsUniqueRelations processing duration: $((New-TimeSpan -Start $startPolicyAssignmentsUniqueRelations -End $endPolicyAssignmentsUniqueRelations).TotalMinutes) minutes ($((New-TimeSpan -Start $startPolicyAssignmentsUniqueRelations -End $endPolicyAssignmentsUniqueRelations).TotalSeconds) seconds)"
#endregion PolicyAssignmentsUniqueRelations
#region PolicyAssignmentsAllCreateEnriched
@@ -18990,7 +19146,7 @@ extensions: [{ name: 'sort' }]
$htmlSUMMARYSecurityGuestUserHighPriviledgesAssignments = $null
$htmlSUMMARYSecurityGuestUserHighPriviledgesAssignments = foreach ($highPrivilegedGuestUserRoleAssignment in ($highPrivilegedGuestUserRoleAssignments)) {
if ($highPrivilededGuestUserRoleAssignment.AssignmentType -eq 'indirect') {
- $assignmentInfo = "indirect / AAD Group Membership '$($highPrivilededGuestUserRoleAssignment.AssignmentInheritFrom)'"
+ $assignmentInfo = "indirect / Microsoft Entra group membership '$($highPrivilededGuestUserRoleAssignment.AssignmentInheritFrom)'"
}
else {
$assignmentInfo = 'direct'
@@ -19617,14 +19773,14 @@ extensions: [{ name: 'sort' }]
#region SUMMARYMGdefault
Write-Host ' processing TenantSummary ManagementGroups - default Management Group'
[void]$htmlTenantSummary.AppendLine(@"
- Hierarchy Settings | Default Management Group Id: '$($defaultManagementGroupId) ' docs
+ Hierarchy Settings | Default Management Group Id: '$($defaultManagementGroupId) ' learn
"@)
#endregion SUMMARYMGdefault
#region SUMMARYMGRequireAuthorizationForGroupCreation
Write-Host ' processing TenantSummary ManagementGroups - requireAuthorizationForGroupCreation Management Group'
[void]$htmlTenantSummary.AppendLine(@"
- Hierarchy Settings | Require authorization for Management Group creation: '$($requireAuthorizationForGroupCreation) ' docs
+ Hierarchy Settings | Require authorization for Management Group creation: '$($requireAuthorizationForGroupCreation) ' learn
"@)
#endregion SUMMARYMGRequireAuthorizationForGroupCreation
@@ -19667,12 +19823,12 @@ extensions: [{ name: 'sort' }]
$tfCount = $summarySubscriptionsCount
$htmlTableId = 'TenantSummary_subs'
- $abbr = " "
+ $abbr = " "
[void]$htmlTenantSummary.AppendLine(@"
$($summarySubscriptionsCount) Subscriptions (state: enabled)
-
Supported Microsoft Azure offers docs
-
Understand Microsoft Defender for Cloud Secure Score Video ,
Blog ,
docs
+
Supported Microsoft Azure offers learn
+
Understand Microsoft Defender for Cloud Secure Score Video ,
Blog ,
learn
Download CSV
semicolon |
comma
@@ -20074,11 +20230,11 @@ extensions: [{ name: 'sort' }]
$tfCount = $tagsUsageCount
$htmlTableId = 'TenantSummary_tagsUsage'
[void]$htmlTenantSummary.AppendLine(@"
- Tag Name Usage ($tagNamesUniqueCount unique Tag Names applied at $($tagNamesUsedInScopes))
-
-
Resource naming and tagging decision guide docs
-
Download CSV
semicolon |
comma
-
+ Tag Name Usage ($tagNamesUniqueCount unique Tag Names applied at $($tagNamesUsedInScopes))
+
+
Resource naming and tagging decision guide learn
+
Download CSV
semicolon |
comma
+
Scope
@@ -20151,7 +20307,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- Tag Name Usage ($tagsUsageCount Tags) docs
+ Tag Name Usage ($tagsUsageCount Tags) learn
"@)
}
#endregion SUMMARYTagNameUsage
@@ -20477,7 +20633,7 @@ extensions: [{ name: 'sort' }]
CAF Naming Recommendation Compliance
-
CAF - Recommended abbreviations for Azure resource types docs
+
CAF - Recommended abbreviations for Azure resource types learn
Resource details can be found in the CSV output *_ResourcesAll.csv
Download CSV
semicolon |
comma
@@ -21086,7 +21242,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$tfCount enabled Subscriptions Features
-
Set up preview features in Azure subscription docs
+
Set up preview features in Azure subscription learn
Download CSV
semicolon |
comma
@@ -21163,7 +21319,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@'
- No enabled Subscriptions Features docs
+ No enabled Subscriptions Features learn
'@)
}
$endSubFeatures = Get-Date
@@ -21190,7 +21346,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
Resource Locks
-
Considerations before applying locks docs
+
Considerations before applying locks learn
Note: Detailed information on Resource Locks is provided in the *_ResourceLocks.csv
@@ -21259,7 +21415,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@'
- No Resource Locks at all docs
+ No Resource Locks at all learn
'@)
}
$endResourceLocks = Get-Date
@@ -21279,8 +21435,8 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
Microsoft Defender for Cloud plans - Subscriptions skipped
-
Register Resource Provider 'Microsoft.Security' docs
-
Microsoft Defender for Cloud's enhanced security features docs
+
Register Resource Provider 'Microsoft.Security' learn
+
Microsoft Defender for Cloud's enhanced security features learn
Download CSV
semicolon |
comma
@@ -21378,17 +21534,17 @@ paging: {results_per_page: ['Records: ', [$spectrum]]},/*state: {types: ['local_
if ($defenderPlanDeprecatedContainerRegistry) {
[void]$htmlTenantSummary.AppendLine(@'
- Using deprecated plan 'Container registries' docs
+ Using deprecated plan 'Container registries' learn
'@)
}
if ($defenderPlanDeprecatedKubernetesService) {
[void]$htmlTenantSummary.AppendLine(@'
- Using deprecated plan 'Kubernetes' docs
+ Using deprecated plan 'Kubernetes' learn
'@)
}
[void]$htmlTenantSummary.AppendLine(@"
- Microsoft Defender for Cloud's enhanced security features docs
+ Microsoft Defender for Cloud's enhanced security features learn
Download CSV semicolon | comma
@@ -21460,17 +21616,17 @@ paging: {results_per_page: ['Records: ', [$spectrum]]},/*state: {types: ['local_
if ($defenderPlanDeprecatedContainerRegistry) {
[void]$htmlTenantSummary.AppendLine(@'
- Using deprecated plan 'Container registries' docs
+ Using deprecated plan 'Container registries' learn
'@)
}
if ($defenderPlanDeprecatedKubernetesService) {
[void]$htmlTenantSummary.AppendLine(@'
- Using deprecated plan 'Kubernetes' docs
+ Using deprecated plan 'Kubernetes' learn
'@)
}
[void]$htmlTenantSummary.AppendLine(@"
- Microsoft Defender for Cloud's enhanced security features docs
+ Microsoft Defender for Cloud's enhanced security features learn
Download CSV semicolon | comma
@@ -21637,7 +21793,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
UserAssigned Managed Identities assigned to Resources / vice versa
-
Managed identity 'user-assigned' vs 'system-assigned' docs
+
Managed identity 'user-assigned' vs 'system-assigned' learn
Download CSV
semicolon |
comma
@@ -23102,7 +23258,7 @@ btn_reset: true, highlight_keywords: true, alternate_rows: true, auto_filter: {
[void]$htmlTenantSummary.AppendLine(@"
$diagnosticSettingsMgManagementGroupsCount ($mgsDiagnosticsApplicableCount) Management Groups configured for Diagnostic settings ($diagnosticSettingsMgCount settings)
-
Management Group Diagnostic Settings - Create Or Update - REST API docs
+
Management Group Diagnostic Settings - Create Or Update - REST API learn
Download CSV
semicolon |
comma
@@ -23240,7 +23396,7 @@ extensions: [{ name: 'sort' }]
else {
[void]$htmlTenantSummary.AppendLine(@'
- No Management Groups configured for Diagnostic settings docs
+ No Management Groups configured for Diagnostic settings learn
'@)
}
@@ -23249,9 +23405,9 @@ extensions: [{ name: 'sort' }]
$tfCount = $arrayMgsWithoutDiagnosticsCount
$htmlTableId = 'TenantSummary_NoDiagnosticsManagementGroups'
[void]$htmlTenantSummary.AppendLine(@"
- $arrayMgsWithoutDiagnosticsCount Management Groups NOT configured for Diagnostic settings docs
+ $arrayMgsWithoutDiagnosticsCount Management Groups NOT configured for Diagnostic settings learn
-
Management Group Diagnostic Settings - Create Or Update - REST API docs
+
Management Group Diagnostic Settings - Create Or Update - REST API learn
Download CSV
semicolon |
comma
@@ -23326,7 +23482,7 @@ extensions: [{ name: 'sort' }]
else {
[void]$htmlTenantSummary.AppendLine(@'
- All Management Groups are configured for Diagnostic settings docs
+ All Management Groups are configured for Diagnostic settings learn
'@)
}
#endregion SUMMARYDiagnosticsManagementGroups
@@ -23346,7 +23502,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$diagnosticSettingsSubSubscriptionsCount Subscriptions configured for Diagnostic settings ($diagnosticSettingsSubCount settings)
-
Create diagnostic setting docs
+
Create diagnostic setting learn
Download CSV
semicolon |
comma
@@ -23480,7 +23636,7 @@ extensions: [{ name: 'sort' }]
else {
[void]$htmlTenantSummary.AppendLine(@'
- No Subscriptions configured for Diagnostic settings docs
+ No Subscriptions configured for Diagnostic settings learn
'@)
}
@@ -23491,7 +23647,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$diagnosticSettingsSubNoDiagCount Subscriptions NOT configured for Diagnostic settings
-
Create diagnostic setting docs
+
Create diagnostic setting learn
Download CSV
semicolon |
comma
@@ -23566,7 +23722,7 @@ extensions: [{ name: 'sort' }]
else {
[void]$htmlTenantSummary.AppendLine(@'
- All Subscriptions are configured for Diagnostic settings docs
+ All Subscriptions are configured for Diagnostic settings learn
'@)
}
#endregion SUMMARYDiagnosticsSubscriptions
@@ -23593,7 +23749,7 @@ extensions: [{ name: 'sort' }]
Resources (1st party) Diagnostics capable $resourceTypesDiagnosticsMetricsLogsTrueCount/$resourceTypesDiagnosticsArraySortedCount ResourceTypes ($resourceTypesDiagnosticsMetricsTrueCount Metrics, $resourceTypesDiagnosticsLogsTrueCount Logs)
Create Custom Policies for Azure ResourceTypes that support Diagnostics Logs and Metrics Create-AzDiagPolicy
-
Supported categories for Azure Resource Logs docs
+
Supported categories for Azure Resource Logs learn
Download CSV
semicolon |
comma
@@ -23917,7 +24073,7 @@ extensions: [{ name: 'sort' }]
else {
$resourceCount = '0'
}
- $recommendation = "Create diagnostics policy for this ResourceType. To verify GA check docs "
+ $recommendation = "Create diagnostics policy for this ResourceType. To verify GA check learn "
$null = $diagnosticsPolicyAnalysis.Add([PSCustomObject]@{
Priority = '2-Medium'
PolicyId = 'n/a'
@@ -23952,7 +24108,7 @@ extensions: [{ name: 'sort' }]
ResourceDiagnostics for Logs - Policy Lifecycle recommendations
Create Custom Policies for Azure ResourceTypes that support Diagnostics Logs and Metrics Create-AzDiagPolicy
-
Supported categories for Azure Resource Logs docs
+
Supported categories for Azure Resource Logs learn
@@ -23984,7 +24140,7 @@ extensions: [{ name: 'sort' }]
$($diagnosticsFinding.Recommendation)
- $($diagnosticsFinding.ResourceType)
+ $($diagnosticsFinding.ResourceType)
$($diagnosticsFinding.ResourceTypeCount)
@@ -24129,24 +24285,24 @@ extensions: [{ name: 'sort' }]
#policySets
if ($tenantCustompolicySetsCount -gt (($LimitPOLICYPolicySetDefinitionsScopedTenant * $LimitCriticalPercentage) / 100)) {
[void]$htmlTenantSummary.AppendLine(@"
- PolicySet definitions: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant docs
+ PolicySet definitions: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant learn
"@)
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- PolicySet definitions: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant docs
+ PolicySet definitions: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant learn
"@)
}
#CustomRoleDefinitions
if ($tenantCustomRolesCount -gt (($LimitRBACCustomRoleDefinitionsTenant * $LimitCriticalPercentage) / 100)) {
[void]$htmlTenantSummary.AppendLine(@"
- Custom Role definitions: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant docs
+ Custom Role definitions: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant learn
"@)
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- Custom Role definitions: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant docs
+ Custom Role definitions: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant learn
"@)
}
@@ -24166,7 +24322,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($mgsApproachingLimitPolicyAssignments | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyAssignmentsManagementGroup) for PolicyAssignment
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -24239,7 +24395,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($mgsApproachingLimitPolicyAssignments | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyAssignmentsManagementGroup) for PolicyAssignment docs
+ $(($mgsApproachingLimitPolicyAssignments | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyAssignmentsManagementGroup) for PolicyAssignment learn
"@)
}
#endregion SUMMARYMgsapproachingLimitsPolicyAssignments
@@ -24253,7 +24409,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($mgsApproachingLimitPolicyScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyDefinitionsScopedManagementGroup) for Policy Scope
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -24326,7 +24482,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $($mgsApproachingLimitPolicyScope.count) Management Groups approaching Limit ($LimitPOLICYPolicyDefinitionsScopedManagementGroup) for Policy Scope docs
+ $($mgsApproachingLimitPolicyScope.count) Management Groups approaching Limit ($LimitPOLICYPolicyDefinitionsScopedManagementGroup) for Policy Scope learn
"@)
}
#endregion SUMMARYMgsapproachingLimitsPolicyScope
@@ -24340,7 +24496,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($mgsApproachingLimitPolicySetScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedManagementGroup) for PolicySet Scope
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -24413,7 +24569,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($mgsApproachingLimitPolicySetScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedManagementGroup) for PolicySet Scope docs
+ $(($mgsApproachingLimitPolicySetScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedManagementGroup) for PolicySet Scope learn
"@)
}
#endregion SUMMARYMgsapproachingLimitsPolicySetScope
@@ -24428,7 +24584,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($mgsApproachingRoleAssignmentLimit | Measure-Object).count) Management Groups approaching Limit ($LimitRBACRoleAssignmentsManagementGroup) for RoleAssignment
-
Azure RBAC Limits docs
+
Azure RBAC Limits learn
Download CSV
semicolon |
comma
@@ -24501,7 +24657,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($mgApproachingRoleAssignmentLimit | Measure-Object).count) Management Groups approaching Limit ($LimitRBACRoleAssignmentsManagementGroup) for RoleAssignment docs
+ $(($mgApproachingRoleAssignmentLimit | Measure-Object).count) Management Groups approaching Limit ($LimitRBACRoleAssignmentsManagementGroup) for RoleAssignment learn
"@)
}
#endregion SUMMARYMgsapproachingLimitsRoleAssignment
@@ -24522,7 +24678,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingLimitFromResourceGroupsAll | Measure-Object).count) Subscriptions approaching Limit ($LimitResourceGroups) for ResourceGroups
-
Azure Subscription Resource Group Limit docs
+
Azure Subscription Resource Group Limit learn
Download CSV
semicolon |
comma
@@ -24596,7 +24752,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($subscriptionsApproachingLimitFromResourceGroupsAll | Measure-Object).count) Subscriptions approaching Limit ($LimitResourceGroups) for ResourceGroups docs
+ $(($subscriptionsApproachingLimitFromResourceGroupsAll | Measure-Object).count) Subscriptions approaching Limit ($LimitResourceGroups) for ResourceGroups learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsResourceGroups
@@ -24610,7 +24766,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingLimitTags | Measure-Object).count) Subscriptions approaching Limit ($LimitTagsSubscription) for Tags
-
Azure Subscription Tag Limit docs
+
Azure Subscription Tag Limit learn
Download CSV
semicolon |
comma
@@ -24683,7 +24839,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $($subscriptionsApproachingLimitTags.count) Subscriptions approaching Limit ($LimitTagsSubscription) for Tags docs
+ $($subscriptionsApproachingLimitTags.count) Subscriptions approaching Limit ($LimitTagsSubscription) for Tags learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsSubscriptionTags
@@ -24697,7 +24853,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingLimitPolicyAssignments | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyAssignmentsSubscription) for PolicyAssignment
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -24770,7 +24926,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($subscriptionsApproachingLimitPolicyAssignments | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyAssignmentsSubscription) for PolicyAssignment docs
+ $(($subscriptionsApproachingLimitPolicyAssignments | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyAssignmentsSubscription) for PolicyAssignment learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsPolicyAssignments
@@ -24784,7 +24940,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyDefinitionsScopedSubscription) for Policy Scope
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -24857,7 +25013,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $($subscriptionsApproachingLimitPolicyScope.count) Subscriptions approaching Limit ($LimitPOLICYPolicyDefinitionsScopedSubscription) for Policy Scope docs
+ $($subscriptionsApproachingLimitPolicyScope.count) Subscriptions approaching Limit ($LimitPOLICYPolicyDefinitionsScopedSubscription) for Policy Scope learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsPolicyScope
@@ -24871,7 +25027,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedSubscription) for PolicySet Scope
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -24944,7 +25100,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedSubscription) for PolicySet Scope docs
+ $(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedSubscription) for PolicySet Scope learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsPolicySetScope
@@ -24961,7 +25117,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingRoleAssignmentLimit | Measure-Object).count) Subscriptions approaching Limit ($($availableSubscriptionsRoleAssignmentLimits)) for RoleAssignment
-
Azure RBAC Limits docs
+
Azure RBAC Limits learn
Download CSV
semicolon |
comma
@@ -25034,7 +25190,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($subscriptionsApproachingRoleAssignmentLimit | Measure-Object).count) Subscriptions approaching Limit ($availableSubscriptionsRoleAssignmentLimits) for RoleAssignment docs
+ $(($subscriptionsApproachingRoleAssignmentLimit | Measure-Object).count) Subscriptions approaching Limit ($availableSubscriptionsRoleAssignmentLimits) for RoleAssignment learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsRoleAssignment
@@ -25275,7 +25431,7 @@ tf.init();}}
}
}
else {
- #https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regex.escape
+ #https://learn.microsoft.com/dotnet/api/system.text.regularexpressions.regex.escape
$s1 = $altName -replace '.*/providers/'
$rm = $s1 -replace '.*/'
$resourceType = $s1 -replace "/$([System.Text.RegularExpressions.Regex]::Escape($rm))"
@@ -28143,7 +28299,7 @@ function runInfo {
}
if (-not $NoAADGroupsResolveMembers) {
- Write-Host " AAD Groups resolve members enabled (honors parameter -DoNotShowRoleAssignmentsUserData) - use parameter: '-NoAADGroupsResolveMembers' to disable resolving AAD Group memberships" -ForegroundColor Yellow
+ Write-Host " Microsoft Entra groups resolve members enabled (honors parameter -DoNotShowRoleAssignmentsUserData) - use parameter: '-NoAADGroupsResolveMembers' to disable resolving group memberships" -ForegroundColor Yellow
$script:paramsUsed += 'NoAADGroupsResolveMembers: false
'
if ($AADGroupMembersLimit -eq 500) {
Write-Host " AADGroupMembersLimit = $AADGroupMembersLimit" -ForegroundColor Yellow
@@ -28155,7 +28311,7 @@ function runInfo {
}
}
else {
- Write-Host " AAD Groups resolve members disabled (-NoAADGroupsResolveMembers = $($NoAADGroupsResolveMembers))" -ForegroundColor Green
+ Write-Host " Microsoft Entra groups resolve members disabled (-NoAADGroupsResolveMembers = $($NoAADGroupsResolveMembers))" -ForegroundColor Green
$script:paramsUsed += 'NoAADGroupsResolveMembers: true
'
}
@@ -29265,7 +29421,7 @@ function dataCollectionDefenderPlans {
)
$currentTask = "Getting Microsoft Defender for Cloud plans for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
+ #https://learn.microsoft.com/rest/api/defenderforcloud/pricings
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Security/pricings?api-version=2018-06-01"
$method = 'GET'
$defenderPlansResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
@@ -29350,7 +29506,7 @@ function dataCollectionDefenderEmailContacts {
)
$currentTask = "Getting Microsoft Defender for Cloud Email contacts for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
+ #https://learn.microsoft.com/rest/api/defenderforcloud/security-contacts
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Security/securityContacts?api-version=2020-01-01-preview"
$method = 'GET'
$defenderSecurityContactsResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -listenOn 'Content' -currentTask $currentTask -caller 'CustomDataCollection'
@@ -29447,7 +29603,6 @@ function dataCollectionVNets {
)
$currentTask = "Getting Virtual Networks for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Network/virtualNetworks?api-version=2022-05-01"
$method = 'GET'
$networkResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
@@ -29474,7 +29629,6 @@ function dataCollectionPrivateEndpoints {
)
$currentTask = "Getting Private Endpoints for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Network/privateEndpoints?api-version=2022-05-01"
$method = 'GET'
$privateEndpointsResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection' -unhandledErrorAction Continue
@@ -29750,15 +29904,19 @@ function dataCollectionResources {
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/resources?`$expand=createdTime,changedTime,properties&api-version=2023-07-01"
$method = 'GET'
$resourcesSubscriptionResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
- #Write-Host 'arm resList count:'$resourcesSubscriptionResult.Count
#endregion resources LIST
#region resources GET
if ($resourcesSubscriptionResult.Count -gt 0) {
$arrayResourcesWithProperties = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
- $resourcesSubscriptionResult | ForEach-Object -Parallel {
- $resource = $_
+ $batchSize = [math]::ceiling($resourcesSubscriptionResult.Count / $azAPICallConf['htParameters'].ThrottleLimit)
+ #Write-Host "Optimal batch size: $($batchSize)"
+ $counterBatch = [PSCustomObject] @{ Value = 0 }
+ $resourcesSubscriptionResultBatch = ($resourcesSubscriptionResult) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ #Write-Host "Processing data in $($resourcesSubscriptionResultBatch.Count) batches"
+
+ $resourcesSubscriptionResultBatch | ForEach-Object -Parallel {
#region using
$arrayResourcesWithProperties = $using:arrayResourcesWithProperties
$htResourceProvidersRef = $using:htResourceProvidersRef
@@ -29772,65 +29930,62 @@ function dataCollectionResources {
#$htResourcesWithProperties = $using:htResourcesWithProperties
#endregion using
- if ($htAvailablePrivateEndpointTypes.(($resource.type).ToLower())) {
- #Write-Host "$($resource.type) in `$htAvailablePrivateEndpointTypes"
- if ($htResourceProvidersRef.($resource.type)) {
- if ($htResourceProvidersRef.($resource.type).APIDefault) {
- $apiVersionToUse = $htResourceProvidersRef.($resource.type).APIDefault
- $apiRef = 'default'
- }
- else {
- $apiVersionToUse = $htResourceProvidersRef.($resource.type).APILatest
- $apiRef = 'latest'
- }
+ foreach ($resource in $_.Group) {
- $currentTask = "Getting Resource Properties API-version: '$apiVersionToUse' ($apiRef); ResourceType: '$($resource.type)'; ResourceId: '$($resource.id)'"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)$($resource.id)?api-version=$apiVersionToUse"
- $method = 'GET'
- $resourceResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection' -listenOn Content -unhandledErrorAction Continue
-
- if ($resourceResult -ne 'ResourceOrResourcegroupNotFound' -and $resourceResult -ne 'convertfromJSONError') {
- $null = $script:arrayResourcesWithProperties.Add($resourceResult)
- #$script:htResourcesWithProperties.($resourceResult.id) = $resourceResult
- if ($resourceResult.properties.privateEndpointConnections.Count -gt 0) {
- foreach ($privateEndpointConnection in $resourceResult.properties.privateEndpointConnections) {
- $resourceResultIdSplit = $resourceResult.id -split '/'
- $null = $script:arrayPrivateEndPointsFromResourceProperties.Add([PSCustomObject]@{
- ResourceName = $resourceResult.name
- ResourceType = $resourceResult.type
- ResourceId = $resourceResult.id
- ResourceResourceGroup = $resourceResultIdSplit[4]
- ResourceSubscriptionId = $scopeId
- ResourceSubscriptionName = $scopeDisplayName
- ResourceMGPath = $ChildMgParentNameChainDelimited
- privateEndpointConnection = $privateEndpointConnection
- })
+ if ($htAvailablePrivateEndpointTypes.(($resource.type).ToLower())) {
+ if ($htResourceProvidersRef.($resource.type)) {
+ if ($htResourceProvidersRef.($resource.type).APIDefault) {
+ $apiVersionToUse = $htResourceProvidersRef.($resource.type).APIDefault
+ $apiRef = 'default'
+ }
+ else {
+ $apiVersionToUse = $htResourceProvidersRef.($resource.type).APILatest
+ $apiRef = 'latest'
+ }
+
+ $currentTask = "Getting Resource Properties API-version: '$apiVersionToUse' ($apiRef); ResourceType: '$($resource.type)'; ResourceId: '$($resource.id)'"
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)$($resource.id)?api-version=$apiVersionToUse"
+ $method = 'GET'
+ $resourceResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection' -listenOn Content -unhandledErrorAction Continue
+
+ if ($resourceResult -ne 'ResourceOrResourcegroupNotFound' -and $resourceResult -ne 'convertfromJSONError') {
+ $null = $script:arrayResourcesWithProperties.Add($resourceResult)
+ #$script:htResourcesWithProperties.($resourceResult.id) = $resourceResult
+ if ($resourceResult.properties.privateEndpointConnections.Count -gt 0) {
+ foreach ($privateEndpointConnection in $resourceResult.properties.privateEndpointConnections) {
+ $resourceResultIdSplit = $resourceResult.id -split '/'
+ $null = $script:arrayPrivateEndPointsFromResourceProperties.Add([PSCustomObject]@{
+ ResourceName = $resourceResult.name
+ ResourceType = $resourceResult.type
+ ResourceId = $resourceResult.id
+ ResourceResourceGroup = $resourceResultIdSplit[4]
+ ResourceSubscriptionId = $scopeId
+ ResourceSubscriptionName = $scopeDisplayName
+ ResourceMGPath = $ChildMgParentNameChainDelimited
+ privateEndpointConnection = $privateEndpointConnection
+ })
+ }
+ }
+ }
+ else {
+ if ($resourceResult -eq 'convertfromJSONError') {
+ $script:htResourcePropertiesConvertfromJSONFailed.($resource.id) = @{}
}
}
}
else {
- if ($resourceResult -eq 'convertfromJSONError') {
- $script:htResourcePropertiesConvertfromJSONFailed.($resource.id) = @{}
- }
+ Write-Host "[Azure Governance Visualizer] Please file an issue at the Azure Governance Visualizer GitHub repository (aka.ms/AzGovViz) and provide this information (scrub subscription Id and company identifyable names): No API-version matches! ResourceType: '$($resource.type)'; ResourceId: '$($resource.id)' - Thank you!" -ForegroundColor DarkRed
}
}
else {
- Write-Host "[Azure Governance Visualizer] Please file an issue at the Azure Governance Visualizer GitHub repository (aka.ms/AzGovViz) and provide this information (scrub subscription Id and company identifyable names): No API-version matches! ResourceType: '$($resource.type)'; ResourceId: '$($resource.id)' - Thank you!" -ForegroundColor DarkRed
+ #Write-Host "$($resource.type) not in `$htAvailablePrivateEndpointTypes"
}
}
- else {
- #Write-Host "$($resource.type) not in `$htAvailablePrivateEndpointTypes"
- }
} -ThrottleLimit $azAPICallConf['htParameters'].ThrottleLimit
}
- #Write-Host 'arm resGet count:' $arrayResourcesWithProperties.Count
#endregion resources GET
- # if ($resourcesSubscriptionResult.Count -ne $arrayResourcesWithProperties.Count) {
- # Write-Host " FYI: Getting Resources for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId'] - ARM list count: $($resourcesSubscriptionResult.Count); ARG get count: $($arrayResourcesWithProperties.Count)"
- # }
-
#region PSRule
if ($azAPICallConf['htParameters'].DoPSRule -eq $true) {
if ($resourcesSubscriptionResult.Count -gt 0) {
@@ -30493,7 +30648,6 @@ function dataCollectionResources {
foreach ($naming in $namingConvention) {
if (($resource.name).StartsWith($naming, 'CurrentCultureIgnoreCase')) {
$cafResourceNamingCheck = 'passed'
- #$applicableNaming = $naming
}
}
}
@@ -30598,7 +30752,6 @@ function dataCollectionResourceGroups {
$subscriptionQuotaId
)
- #https://management.azure.com/subscriptions/{subscriptionId}/resourcegroups?api-version=2020-06-01
$currentTask = "Getting ResourceGroups for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/resourcegroups?api-version=2021-04-01"
$method = 'GET'
@@ -31433,12 +31586,11 @@ function dataCollectionPolicyDefinitions {
if (-not [string]::IsNullOrEmpty($roledefinitionId)) {
if (-not $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId)) {
$script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId) = @{}
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [array]$hlpPolicyDefinitionId
+ $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [System.Collections.ArrayList]@()
+ $null = $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies.Add($hlpPolicyDefinitionId)
}
else {
- $usedInPolicies = $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies
- $usedInPolicies += $hlpPolicyDefinitionId
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = $usedInPolicies
+ $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies.Add($hlpPolicyDefinitionId)
}
}
else {
@@ -32016,7 +32168,6 @@ function dataCollectionPolicyAssignmentsMG {
$policySetCategory = $policySetDefinition.Category
}
else {
- #test
#Write-Host "pa '($L0mgmtGroupPolicyAssignment.Id)' scope: '$($scopeId)' - policySetDefinition not available: $policySetDefinitionId"
Start-Sleep -Seconds 1
}
@@ -32707,11 +32858,11 @@ function dataCollectionRoleDefinitions {
if ($TargetMgOrSub -eq 'Sub') {
$currentTask = "Getting Custom Role definitions for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/roleDefinitions?api-version=2018-07-01&`$filter=type eq 'CustomRole'"
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'CustomRole'"
}
if ($TargetMgOrSub -eq 'MG') {
$currentTask = "Getting Custom Role definitions for Management Group: '$($scopeDisplayName)' ('$scopeId')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($scopeId)/providers/Microsoft.Authorization/roleDefinitions?api-version=2018-07-01&`$filter=type eq 'CustomRole'"
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($scopeId)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'CustomRole'"
}
$method = 'GET'
$scopeCustomRoleDefinitions = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
@@ -32797,7 +32948,6 @@ function dataCollectionRoleAssignmentsMG {
$roleAssignmentScheduleInstances = ($roleAssignmentScheduleInstancesFromAPI.where( { ($_.properties.roleAssignmentScheduleId -replace '.*/') -ne ($_.properties.originRoleAssignmentId -replace '.*/') }))
$roleAssignmentScheduleInstancesCount = $roleAssignmentScheduleInstances.Count
if ($roleAssignmentScheduleInstancesCount -gt 0) {
- #$htRoleAssignmentsPIM = @{}
foreach ($roleAssignmentScheduleInstance in $roleAssignmentScheduleInstances) {
$script:htRoleAssignmentsPIM.($roleAssignmentScheduleInstance.properties.originRoleAssignmentId.tolower()) = $roleAssignmentScheduleInstance.properties
}
@@ -33079,7 +33229,6 @@ function dataCollectionRoleAssignmentsSub {
$roleAssignmentScheduleInstances = ($roleAssignmentScheduleInstancesFromAPI.where( { ($_.properties.roleAssignmentScheduleId -replace '.*/') -ne ($_.properties.originRoleAssignmentId -replace '.*/') }))
$roleAssignmentScheduleInstancesCount = $roleAssignmentScheduleInstances.Count
if ($roleAssignmentScheduleInstancesCount -gt 0) {
- #$htRoleAssignmentsPIM = @{}
foreach ($roleAssignmentScheduleInstance in $roleAssignmentScheduleInstances) {
$script:htRoleAssignmentsPIM.($roleAssignmentScheduleInstance.properties.originRoleAssignmentId.tolower()) = $roleAssignmentScheduleInstance.properties
}
@@ -33674,7 +33823,7 @@ function processScopeInsights($mgChild, $mgChildOf) {
"@
if ($mgId -eq $defaultManagementGroupId) {
$script:html += @'
- Default Management Group docs
+ Default Management Group learn
'@
}
$script:html += @"
@@ -34232,36 +34381,83 @@ if (-not $HierarchyMapOnly) {
#region Getting Available Private Endpoint Types
$startGetAvailablePrivateEndpointTypes = Get-Date
- $currentTask = 'Getting Locations'
- Write-Host $currentTask
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($azAPICallConf['checkcontext'].Subscription.Id)/locations?api-version=2020-01-01"
- $method = 'GET'
- $getLocations = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
- Write-Host " Returned $($getLocations.Count) locations"
+ $subsToProcessForGettingPrivateEndpointTypes = [System.Collections.ArrayList]@()
+ $prioCounter = 0
+ foreach ($subscription in $subsToProcessInCustomDataCollection) {
+ $prioCounter++
+ if ($subscription.subscriptionId -eq $azAPICallConf['checkcontext'].Subscription.Id) {
+ $null = $subsToProcessForGettingPrivateEndpointTypes.Add([PSCustomObject]@{
+ subscriptionInfo = $subscription
+ prio = 0
+ })
+ }
+ else {
+ $null = $subsToProcessForGettingPrivateEndpointTypes.Add([PSCustomObject]@{
+ subscriptionInfo = $subscription
+ prio = $prioCounter
+ })
+ }
+ }
- Write-Host "Getting 'Available Private Endpoint Types' for $($getLocations.Count) locations"
- $getLocations | ForEach-Object -Parallel {
- $location = $_
- $azAPICallConf = $using:azAPICallConf
- $htAvailablePrivateEndpointTypes = $using:htAvailablePrivateEndpointTypes
- $currentTask = "Getting 'Available Private Endpoint Types' for location $($location.name)"
- #Write-Host $currentTask
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($azAPICallConf['checkcontext'].Subscription.Id)/providers/Microsoft.Network/locations/$($location.name)/availablePrivateEndpointTypes?api-version=2022-07-01"
+ foreach ($subscription in $subsToProcessForGettingPrivateEndpointTypes | Sort-Object -Property prio) {
+
+ if ($privateEndpointAvailabilityCheckCompleted) {
+ continue
+ }
+
+ $subscriptionId = $subscription.subscriptionInfo.subscriptionId
+ $subscriptionName = $subscription.subscriptionInfo.subscriptionName
+
+ $currentTask = "Getting Locations for Subscription '$($subscriptionName)' ($($subscriptionId))"
+ Write-Host $currentTask
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($subscriptionId)/locations?api-version=2020-01-01"
$method = 'GET'
- $availablePrivateEndpointTypes = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -skipOnErrorCode 400, 409
- Write-Host " Returned $($availablePrivateEndpointTypes.Count) 'Available Private Endpoint Types' for location $($location.name)"
- foreach ($availablePrivateEndpointType in $availablePrivateEndpointTypes) {
- if (-not $htAvailablePrivateEndpointTypes.(($availablePrivateEndpointType.resourceName).ToLower())) {
- $script:htAvailablePrivateEndpointTypes.(($availablePrivateEndpointType.resourceName).ToLower()) = @{}
+ $getLocations = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
+ Write-Host " Returned $($getLocations.Count) locations"
+
+ Write-Host "Getting 'Available Private Endpoint Types' for Subscription '$($subscriptionName)' ($($subscriptionId)) for $($getLocations.Count) locations"
+
+ $batchSize = [math]::ceiling($getLocations.Count / $ThrottleLimit)
+ Write-Host "Optimal batch size: $($batchSize)"
+ $counterBatch = [PSCustomObject] @{ Value = 0 }
+ $getLocationsBatch = ($getLocations) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ Write-Host "Processing data in $($getLocationsBatch.Count) batches"
+
+ $getLocationsBatch | ForEach-Object -Parallel {
+ $subscriptionId = $using:subscriptionId
+ $azAPICallConf = $using:azAPICallConf
+ $htAvailablePrivateEndpointTypes = $using:htAvailablePrivateEndpointTypes
+
+ foreach ($location in $_.Group) {
+ $currentTask = "Getting 'Available Private Endpoint Types' for location $($location.name)"
+ #Write-Host $currentTask
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($subscriptionId)/providers/Microsoft.Network/locations/$($location.name)/availablePrivateEndpointTypes?api-version=2022-07-01"
+ $method = 'GET'
+ $availablePrivateEndpointTypes = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -skipOnErrorCode 400, 409
+ Write-Host " Returned $($availablePrivateEndpointTypes.Count) 'Available Private Endpoint Types' for location $($location.name)"
+ foreach ($availablePrivateEndpointType in $availablePrivateEndpointTypes) {
+ if (-not $htAvailablePrivateEndpointTypes.(($availablePrivateEndpointType.resourceName).ToLower())) {
+ $script:htAvailablePrivateEndpointTypes.(($availablePrivateEndpointType.resourceName).ToLower()) = @{}
+ }
+ }
}
+ } -ThrottleLimit $ThrottleLimit
+
+ if ($htAvailablePrivateEndpointTypes.Keys.Count -gt 0) {
+ #Write-Host " Created ht for $($htAvailablePrivateEndpointTypes.Keys.Count) 'Available Private Endpoint Types'"
+ $privateEndpointAvailabilityCheckCompleted = $true
}
- } -ThrottleLimit $ThrottleLimit
+ else {
+ Write-Host " $($htAvailablePrivateEndpointTypes.Keys.Count) 'Available Private Endpoint Types' - likely the Resource Provider 'Microsoft.Network' is not registered - trying next available subscription"
+ $privateEndpointAvailabilityCheckCompleted = $false
+ }
+ }
if ($htAvailablePrivateEndpointTypes.Keys.Count -gt 0) {
Write-Host " Created ht for $($htAvailablePrivateEndpointTypes.Keys.Count) 'Available Private Endpoint Types'"
}
else {
- $throwmsg = "$($htAvailablePrivateEndpointTypes.Keys.Count) 'Available Private Endpoint Types' - Please use another Subscription for the AzContext (current subscriptionId: '$($azAPICallConf['checkcontext'].Subscription.Id)') -> use parameter: -SubscriptionId4AzContext ''"
+ $throwmsg = "$($htAvailablePrivateEndpointTypes.Keys.Count) 'Available Private Endpoint Types' - Checked for $($subsToProcessForGettingPrivateEndpointTypes.Count) Subscriptions with no success. Make sure that for at least one Subscription the Resource Provider 'Microsoft.Network' is registered. Once you registered the Resource Provider for Subscription 'subscriptionEnabled' it may be a good idea to use the parameter: -SubscriptionId4AzContext ''"
Write-Host $throwmsg -ForegroundColor DarkRed
Throw $throwmsg
}
diff --git a/pwsh/dev/devAzGovVizParallel.ps1 b/pwsh/dev/devAzGovVizParallel.ps1
index 98dbf86a..b34c55f2 100644
--- a/pwsh/dev/devAzGovVizParallel.ps1
+++ b/pwsh/dev/devAzGovVizParallel.ps1
@@ -62,7 +62,7 @@
use this parameter if Azure Consumption data should not be exported (CSV)
.PARAMETER ThrottleLimit
- Leveraging PowerShell Core´s parallel capability you can define the ThrottleLimit (default=5)
+ Leveraging PowerShell Core's parallel capability you can define the ThrottleLimit (default=5)
.PARAMETER DoTranscript
Log the console output
@@ -127,7 +127,7 @@
Q: Why would you want to do this? A: In larger tenants the ScopeInsights section blows up the html file (up to unusable due to html file size)
.PARAMETER AADGroupMembersLimit
- Defines the limit (default=500) of AAD Group members; For AAD Groups that have more members than the defined limit Group members will not be resolved
+ Defines the limit (default=500) of Microsoft Entra group members; For Microsoft Entra groups that have more members than the defined limit group members will not be resolved
.PARAMETER NoResources
Will speed up the processing time but information like Resource diagnostics capability, resource type stats, UserAssigned Identities assigned to Resources is excluded (featured for large tenants)
@@ -236,7 +236,7 @@
Define for which time period (days) Azure Consumption data should be gathered; e.g. 14 days; default is 1 day
PS C:\>.\AzGovVizParallel.ps1 -ManagementGroupId -AzureConsumptionPeriod 14
- Define the number of script blocks running in parallel. Leveraging PowerShell Core´s parallel capability you can define the ThrottleLimit (default=5)
+ Define the number of script blocks running in parallel. Leveraging PowerShell Core's parallel capability you can define the ThrottleLimit (default=5)
PS C:\>.\AzGovVizParallel.ps1 -ManagementGroupId -ThrottleLimit 10
Define if you want to log the console output
@@ -279,7 +279,7 @@
If the parameter switch is true then the following parameters will be set:
-PolicyAtScopeOnly $true
-RBACAtScopeOnly $true
- - NoResourceProvidersAtAll $true
+ -NoResourceProvidersAtAll $true
-NoScopeInsights $true
PS C:\>.\AzGovVizParallel.ps1 -ManagementGroupId -LargeTenant
@@ -302,7 +302,7 @@
Note if you use parameter -LargeTenant then parameter -NoScopeInsights will be set to true
PS C:\>.\AzGovVizParallel.ps1 -ManagementGroupId -NoScopeInsights
- Defines the limit (default=500) of AAD Group members; For AAD Groups that have more members than the defined limit Group members will not be resolved
+Defines the limit (default=500) of Microsoft Entra group members; For groups that have more members than the defined limit group members will not be resolved
PS C:\>.\AzGovVizParallel.ps1 -ManagementGroupId -AADGroupMembersLimit 750
Will speed up the processing time but information like Resource diagnostics capability, resource type stats, UserAssigned Identities assigned to Resources is excluded (featured for large tenants)
@@ -365,14 +365,14 @@ Param
$Product = 'AzGovViz',
[string]
- $ProductVersion = '6.3.7',
+ $ProductVersion = '6.4.0',
[string]
$GithubRepository = 'aka.ms/AzGovViz',
# <--- AzAPICall related parameters #consult the AzAPICall GitHub repository for details aka.ms/AzAPICall
[string]
- $AzAPICallVersion = '1.1.85',
+ $AzAPICallVersion = '1.2.0',
[switch]
$DebugAzAPICall,
@@ -555,33233 +555,159 @@ Param
$NoALZPolicyVersionChecker,
[switch]
- $NoDefinitionInsightsDedicatedHTML,
-
- [switch]
- $NoStorageAccountAccessAnalysis,
-
- [array]
- $StorageAccountAccessAnalysisSubscriptionTags = @('undefined'),
-
- [array]
- $StorageAccountAccessAnalysisStorageAccountTags = @('undefined'),
-
- [switch]
- $GitHubActionsOIDC,
-
- [switch]
- $NoNetwork,
-
- [int]
- $NetworkSubnetIPAddressUsageCriticalPercentage = 80,
-
- [switch]
- $ShowRunIdentifier,
-
- #https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#role-based-access-control-limits
- [int]
- $LimitRBACCustomRoleDefinitionsTenant = 5000,
-
- [int]
- $LimitRBACRoleAssignmentsManagementGroup = 500,
-
- #https://docs.microsoft.com/en-us/azure/governance/policy/overview#maximum-count-of-azure-policy-objects
- [int]
- $LimitPOLICYPolicyAssignmentsManagementGroup = 200,
-
- [int]
- $LimitPOLICYPolicyAssignmentsSubscription = 200,
-
- [int]
- $LimitPOLICYPolicyDefinitionsScopedManagementGroup = 500,
-
- [int]
- $LimitPOLICYPolicyDefinitionsScopedSubscription = 500,
-
- [int]
- $LimitPOLICYPolicySetAssignmentsManagementGroup = 200,
-
- [int]
- $LimitPOLICYPolicySetAssignmentsSubscription = 200,
-
- [int]
- $LimitPOLICYPolicySetDefinitionsScopedTenant = 2500,
-
- [int]
- $LimitPOLICYPolicySetDefinitionsScopedManagementGroup = 200,
-
- [int]
- $LimitPOLICYPolicySetDefinitionsScopedSubscription = 200,
-
- #https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#subscription-limits
- [int]
- $LimitResourceGroups = 980,
-
- [int]
- $LimitTagsSubscription = 50,
-
- [array]
- $MSTenantIds = @('2f4a9838-26b7-47ee-be60-ccc1fdec5953', '33e01921-4d64-4f8c-a055-5bdaffd5e33d'),
-
- [array]
- $ValidPolicyEffects = @('append', 'audit', 'auditIfNotExists', 'deny', 'denyAction', 'deployIfNotExists', 'modify', 'manual', 'disabled', 'EnforceRegoPolicy', 'enforceSetting')
-)
-
-$Error.clear()
-$ErrorActionPreference = 'Stop'
-#removeNoise
-$ProgressPreference = 'SilentlyContinue'
-Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings 'true'
-
-#start
-$startAzGovViz = Get-Date
-$startTime = Get-Date -Format 'dd-MMM-yyyy HH:mm:ss'
-Write-Host "Start Azure Governance Visualizer $($startTime) (#$($ProductVersion))"
-
-if ($ManagementGroupId -match ' ') {
- Write-Host "Provided Management Group ID: '$($ManagementGroupId)'" -ForegroundColor Yellow
- Write-Host 'The Management Group ID may not contain spaces - provide the Management Group ID, not the displayName.' -ForegroundColor DarkRed
- throw 'Management Group ID validation failed!'
-}
-
-#region Functions
-function addHtParameters {
- Write-Host 'Add Azure Governance Visualizer htParameters'
- if ($LargeTenant -eq $true) {
- $script:NoScopeInsights = $true
- $NoResourceProvidersAtAll = $true
- $PolicyAtScopeOnly = $true
- $RBACAtScopeOnly = $true
- }
-
- if ($ManagementGroupsOnly) {
- $script:NoSingleSubscriptionOutput = $true
- }
-
- if ($HierarchyMapOnly) {
- $NoJsonExport = $true
- }
-
- $script:azAPICallConf['htParameters'] += [ordered]@{
- DoAzureConsumption = [bool]$DoAzureConsumption
- DoNotIncludeResourceGroupsOnPolicy = [bool]$DoNotIncludeResourceGroupsOnPolicy
- DoNotIncludeResourceGroupsAndResourcesOnRBAC = [bool]$DoNotIncludeResourceGroupsAndResourcesOnRBAC
- DoNotShowRoleAssignmentsUserData = [bool]$DoNotShowRoleAssignmentsUserData
- HierarchyMapOnly = [bool]$HierarchyMapOnly
- LargeTenant = [bool]$LargeTenant
- ManagementGroupsOnly = [bool]$ManagementGroupsOnly
- NoJsonExport = [bool]$NoJsonExport
- NoMDfCSecureScore = [bool]$NoMDfCSecureScore
- NoResourceProvidersDetailed = [bool]$NoResourceProvidersDetailed
- NoResourceProvidersAtAll = [bool]$NoResourceProvidersAtAll
- NoPolicyComplianceStates = [bool]$NoPolicyComplianceStates
- NoResources = [bool]$NoResources
- ProductVersion = $ProductVersion
- PolicyAtScopeOnly = [bool]$PolicyAtScopeOnly
- RBACAtScopeOnly = [bool]$RBACAtScopeOnly
- DoPSRule = [bool]$DoPSRule
- PSRuleFailedOnly = [bool]$PSRuleFailedOnly
- NoALZPolicyVersionChecker = [bool]$NoALZPolicyVersionChecker
- NoStorageAccountAccessAnalysis = [bool]$NoStorageAccountAccessAnalysis
- GitHubActionsOIDC = [bool]$GitHubActionsOIDC
- NoNetwork = [bool]$NoNetwork
- ThrottleLimit = $ThrottleLimit
- }
- Write-Host 'htParameters:'
- $azAPICallConf['htParameters'] | Format-Table -AutoSize | Out-String
- Write-Host 'Add Azure Governance Visualizer htParameters succeeded' -ForegroundColor Green
-}
-function addIndexNumberToArray (
- [Parameter(Mandatory = $True)]
- [array]$array
-) {
- for ($i = 0; $i -lt ($array).count; $i++) {
- Add-Member -InputObject $array[$i] -Name '#' -Value ($i + 1) -MemberType NoteProperty
- }
- return $array
-}
-function addRowToTable() {
- Param (
- [string]$level = 0,
- [string]$mgName = '',
- [string]$mgId = '',
- [string]$mgParentId = '',
- [string]$mgParentName = '',
- [string]$mgASCSecureScore = '',
- [string]$Subscription = '',
- [string]$SubscriptionId = '',
- [string]$SubscriptionQuotaId = '',
- [string]$SubscriptionState = '',
- [string]$SubscriptionASCSecureScore = '',
- [string]$SubscriptionTags = '',
- [int]$SubscriptionTagsCount = 0,
- [string]$Policy = '',
- [string]$PolicyAvailability = '',
- [string]$PolicyDescription = '',
- [string]$PolicyVariant = '',
- [string]$PolicyType = '',
- $PolicyIsALZ = '',
- [string]$PolicyCategory = '',
- [string]$PolicyDefinitionIdGuid = '',
- [string]$PolicyDefinitionId = '',
- [string]$PolicyDefintionScope = '',
- [string]$PolicyDefintionScopeMgSub = '',
- [string]$PolicyDefintionScopeId = '',
- [int]$PolicyDefinitionsScopedLimit = 0,
- [int]$PolicyDefinitionsScopedCount = 0,
- [int]$PolicySetDefinitionsScopedLimit = 0,
- [int]$PolicySetDefinitionsScopedCount = 0,
- [string]$PolicyDefinitionEffectDefault = '',
- [string]$PolicyDefinitionEffectFixed = '',
- [string]$PolicyAssignmentScope = '',
- [string]$PolicyAssignmentScopeMgSubRg = '',
- [string]$PolicyAssignmentScopeName = '',
- $PolicyAssignmentNotScopes = '',
- [string]$PolicyAssignmentId = '',
- [string]$PolicyAssignmentName = '',
- [string]$PolicyAssignmentDisplayName = '',
- [string]$PolicyAssignmentDescription = '',
- [string]$PolicyAssignmentEnforcementMode = '',
- $PolicyAssignmentNonComplianceMessages = '',
- [string]$PolicyAssignmentIdentity = '',
- [int]$PolicyAssignmentLimit = 0,
- [int]$PolicyAssignmentCount = 0,
- [int]$PolicyAssignmentAtScopeCount = 0,
- $PolicyAssignmentParameters,
- $PolicyAssignmentParametersFormated,
- [int]$PolicySetAssignmentLimit = 0,
- [int]$PolicySetAssignmentCount = 0,
- [int]$PolicySetAssignmentAtScopeCount = 0,
- [int]$PolicyAndPolicySetAssignmentAtScopeCount = 0,
- [string]$PolicyAssignmentAssignedBy = '',
- [string]$PolicyAssignmentCreatedBy = '',
- [string]$PolicyAssignmentCreatedOn = '',
- [string]$PolicyAssignmentUpdatedBy = '',
- [string]$PolicyAssignmentUpdatedOn = '',
- [string]$RoleDefinitionId = '',
- [string]$RoleDefinitionName = '',
- [string]$RoleAssignmentIdentityDisplayname = '',
- [string]$RoleAssignmentIdentitySignInName = '',
- [string]$RoleAssignmentIdentityObjectId = '',
- [string]$RoleAssignmentIdentityObjectType = '',
- [string]$RoleAssignmentId = '',
- [string]$RoleAssignmentScope = '',
- [string]$RoleAssignmentScopeName = '',
- [string]$RoleAssignmentScopeRG = '',
- [string]$RoleAssignmentScopeRes = '',
- [string]$RoleAssignmentScopeType = '',
- [string]$RoleAssignmentCreatedBy = '',
- [string]$RoleAssignmentCreatedOn = '',
- $RoleAssignmentCreatedOnUnformatted,
- [string]$RoleAssignmentUpdatedBy = '',
- [string]$RoleAssignmentUpdatedOn = '',
- [string]$RoleIsCustom = '',
- [string]$RoleAssignableScopes = '',
- [int]$RoleAssignmentsLimit = 0,
- [int]$RoleAssignmentsCount = 0,
- [string]$RoleActions = '',
- [string]$RoleNotActions = '',
- [string]$RoleDataActions = '',
- [string]$RoleNotDataActions = '',
- $RoleCanDoRoleAssignments,
- [int]$RoleSecurityCustomRoleOwner = 0,
- [int]$RoleSecurityOwnerAssignmentSP = 0,
- [string]$BlueprintName = '',
- [string]$BlueprintId = '',
- [string]$BlueprintDisplayName = '',
- [string]$BlueprintDescription = '',
- [string]$BlueprintScoped = '',
- [string]$BlueprintAssignmentVersion = '',
- [string]$BlueprintAssignmentId = '',
- [string]$RoleAssignmentPIM = '',
- [string]$RoleAssignmentPIMAssignmentType = '',
- $RoleAssignmentPIMSlotStart = '',
- $RoleAssignmentPIMSlotEnd = ''
- )
-
- $null = $script:newTable.Add([PSCustomObject]@{
- level = $level
- mgName = $mgName
- mgId = $mgId
- mgParentId = $mgParentId
- mgParentName = $mgParentName
- mgASCSecureScore = $mgASCSecureScore
- Subscription = $Subscription
- SubscriptionId = $SubscriptionId
- SubscriptionQuotaId = $SubscriptionQuotaId
- SubscriptionState = $SubscriptionState
- SubscriptionASCSecureScore = $SubscriptionASCSecureScore
- SubscriptionTags = $SubscriptionTags
- SubscriptionTagsCount = $SubscriptionTagsCount
- Policy = $Policy
- PolicyAvailability = $PolicyAvailability
- PolicyDescription = $PolicyDescription
- PolicyVariant = $PolicyVariant
- PolicyType = $PolicyType
- PolicyIsALZ = $PolicyIsALZ
- PolicyCategory = $PolicyCategory
- PolicyDefinitionIdGuid = $PolicyDefinitionIdGuid
- PolicyDefinitionId = $PolicyDefinitionId
- PolicyDefintionScope = $PolicyDefintionScope
- PolicyDefintionScopeMgSub = $PolicyDefintionScopeMgSub
- PolicyDefintionScopeId = $PolicyDefintionScopeId
- PolicyDefinitionsScopedLimit = $PolicyDefinitionsScopedLimit
- PolicyDefinitionsScopedCount = $PolicyDefinitionsScopedCount
- PolicySetDefinitionsScopedLimit = $PolicySetDefinitionsScopedLimit
- PolicySetDefinitionsScopedCount = $PolicySetDefinitionsScopedCount
- PolicyDefinitionEffectDefault = $PolicyDefinitionEffectDefault
- PolicyDefinitionEffectFixed = $PolicyDefinitionEffectFixed
- PolicyAssignmentScope = $PolicyAssignmentScope
- PolicyAssignmentScopeMgSubRg = $PolicyAssignmentScopeMgSubRg
- PolicyAssignmentScopeName = $PolicyAssignmentScopeName
- PolicyAssignmentNotScopes = $PolicyAssignmentNotScopes
- PolicyAssignmentId = $PolicyAssignmentId
- PolicyAssignmentName = $PolicyAssignmentName
- PolicyAssignmentDisplayName = $PolicyAssignmentDisplayName
- PolicyAssignmentDescription = $PolicyAssignmentDescription
- PolicyAssignmentEnforcementMode = $PolicyAssignmentEnforcementMode
- PolicyAssignmentNonComplianceMessages = $PolicyAssignmentNonComplianceMessages
- PolicyAssignmentIdentity = $PolicyAssignmentIdentity
- PolicyAssignmentLimit = $PolicyAssignmentLimit
- PolicyAssignmentCount = $PolicyAssignmentCount
- PolicyAssignmentAtScopeCount = $PolicyAssignmentAtScopeCount
- PolicyAssignmentParameters = $PolicyAssignmentParameters
- PolicyAssignmentParametersFormated = $PolicyAssignmentParametersFormated
- PolicySetAssignmentLimit = $PolicySetAssignmentLimit
- PolicySetAssignmentCount = $PolicySetAssignmentCount
- PolicySetAssignmentAtScopeCount = $PolicySetAssignmentAtScopeCount
- PolicyAndPolicySetAssignmentAtScopeCount = $PolicyAndPolicySetAssignmentAtScopeCount
- PolicyAssignmentAssignedBy = $PolicyAssignmentAssignedBy
- PolicyAssignmentCreatedBy = $PolicyAssignmentCreatedBy
- PolicyAssignmentCreatedOn = $PolicyAssignmentCreatedOn
- PolicyAssignmentUpdatedBy = $PolicyAssignmentUpdatedBy
- PolicyAssignmentUpdatedOn = $PolicyAssignmentUpdatedOn
- RoleDefinitionId = $RoleDefinitionId
- RoleDefinitionName = $RoleDefinitionName
- RoleAssignmentIdentityDisplayname = $RoleAssignmentIdentityDisplayname
- RoleAssignmentIdentitySignInName = $RoleAssignmentIdentitySignInName
- RoleAssignmentIdentityObjectId = $RoleAssignmentIdentityObjectId
- RoleAssignmentIdentityObjectType = $RoleAssignmentIdentityObjectType
- RoleAssignmentId = $RoleAssignmentId
- RoleAssignmentScope = $RoleAssignmentScope
- RoleAssignmentScopeName = $RoleAssignmentScopeName
- RoleAssignmentScopeRG = $RoleAssignmentScopeRG
- RoleAssignmentScopeRes = $RoleAssignmentScopeRes
- RoleAssignmentScopeType = $RoleAssignmentScopeType
- RoleIsCustom = $RoleIsCustom
- RoleAssignableScopes = $RoleAssignableScopes
- RoleAssignmentCreatedBy = $RoleAssignmentCreatedBy
- RoleAssignmentCreatedOn = $RoleAssignmentCreatedOn
- RoleAssignmentCreatedOnUnformatted = $RoleAssignmentCreatedOnUnformatted
- RoleAssignmentUpdatedBy = $RoleAssignmentUpdatedBy
- RoleAssignmentUpdatedOn = $RoleAssignmentUpdatedOn
- RoleAssignmentsLimit = $RoleAssignmentsLimit
- RoleAssignmentsCount = $RoleAssignmentsCount
- RoleActions = $RoleActions
- RoleNotActions = $RoleNotActions
- RoleDataActions = $RoleDataActions
- RoleNotDataActions = $RoleNotDataActions
- RoleCanDoRoleAssignments = $RoleCanDoRoleAssignments
- RoleSecurityCustomRoleOwner = $RoleSecurityCustomRoleOwner
- RoleSecurityOwnerAssignmentSP = $RoleSecurityOwnerAssignmentSP
- BlueprintName = $BlueprintName
- BlueprintId = $BlueprintId
- BlueprintDisplayName = $BlueprintDisplayName
- BlueprintDescription = $BlueprintDescription
- BlueprintScoped = $BlueprintScoped
- BlueprintAssignmentVersion = $BlueprintAssignmentVersion
- BlueprintAssignmentId = $BlueprintAssignmentId
- RoleAssignmentPIM = $RoleAssignmentPIM
- RoleAssignmentPIMAssignmentType = $RoleAssignmentPIMAssignmentType
- RoleAssignmentPIMSlotStart = $RoleAssignmentPIMSlotStart
- RoleAssignmentPIMSlotEnd = $RoleAssignmentPIMSlotEnd
- })
-}
-function apiCallTracking {
- [CmdletBinding()]Param(
- [string]$stage,
- [string]$spacing
- )
- #APITracking
- $APICallTrackingCount = ($azAPICallConf['arrayAPICallTracking']).Count
- $APICallTrackingRetriesCount = ($azAPICallConf['arrayAPICallTracking'].where({ $_.TryCounter -gt 1 } )).Count
- $APICallTrackingGroupedByTargetEndpoint = $azAPICallConf['arrayAPICallTracking'] | Group-Object -Property TargetEndpoint
- $APICallTrackingRestartDueToDuplicateNextlinkCounterCount = ($azAPICallConf['arrayAPICallTracking'].where({ $_.RestartDueToDuplicateNextlinkCounter -gt 0 } )).Count
- Write-Host "$($spacing)$($stage) API call stats:"
- $duarationStats = ($azAPICallConf['arrayAPICallTracking'].Duration | Measure-Object -Average -Maximum -Minimum)
- Write-Host "$($spacing) API calls total count: $APICallTrackingCount ($APICallTrackingRetriesCount retries; $APICallTrackingRestartDueToDuplicateNextlinkCounterCount nextLinkReset) | average: $($duarationStats.Average) sec, maximum: $($duarationStats.Maximum) sec, minimum: $($duarationStats.Minimum) sec"
- foreach ($targetEndpoint in $APICallTrackingGroupedByTargetEndpoint | Sort-Object -Property Name) {
- $APICallTrackingRetriesCount = ($targetEndpoint.Group.where({ $_.TryCounter -gt 1 } )).Count
- $APICallTrackingRestartDueToDuplicateNextlinkCounterCount = ($targetEndpoint.Group.where({ $_.RestartDueToDuplicateNextlinkCounter -gt 0 } )).Count
- $duarationStats = ($targetEndpoint.Group.Duration | Measure-Object -Average -Maximum -Minimum)
- Write-Host "$($spacing) API calls endpoint '$($targetEndpoint.Name) ($($azAPICallConf['azAPIEndpointUrls'].($targetEndpoint.Name)))' count: $($targetEndpoint.Count) ($APICallTrackingRetriesCount retries; $APICallTrackingRestartDueToDuplicateNextlinkCounterCount nextLinkReset) | average: $($duarationStats.Average) sec, maximum: $($duarationStats.Maximum) sec, minimum: $($duarationStats.Minimum) sec"
- }
-}
-function buildJSON {
- #$fileTimestamp = Get-Date -Format "yyyyMM-dd HHmmss"
- $startJSON = Get-Date
- $startBuildHt = Get-Date
-
- Write-Host 'Create Hierarchy JSON'
- Write-Host ' Create ht for JSON'
-
- $htJSON = [ordered]@{}
- $htJSON.ManagementGroups = [ordered]@{}
-
- $MgIds = ($optimizedTableForPathQuery) | Select-Object -Property level, MgId, MgName, mgParentId, mgParentName | Sort-Object -Property level, MgId -Unique
- $grpScopePolicyDefinitionsCustom = (($htCacheDefinitionsPolicy).values).where( { $_.Type -eq 'Custom' }) | Group-Object ScopeMgSub
- $grpMgScopePolicyDefinitionsCustom = ($grpScopePolicyDefinitionsCustom.where( { $_.Name -eq 'Mg' }).Group | Sort-Object -Property PolicyDefinitionId | Group-Object ScopeId)
- $grpSubScopePolicyDefinitionsCustom = ($grpScopePolicyDefinitionsCustom.where( { $_.Name -eq 'Sub' }).Group | Sort-Object -Property PolicyDefinitionId | Group-Object ScopeId)
-
- $grpScopePolicySetDefinitionsCustom = (($htCacheDefinitionsPolicySet).values).where( { $_.Type -eq 'Custom' }) | Group-Object ScopeMgSub
- $grpMgScopePolicySetDefinitionsCustom = $grpScopePolicySetDefinitionsCustom.where( { $_.Name -eq 'Mg' }).Group | Sort-Object -Property PolicyDefinitionId | Group-Object ScopeId
- $grpSubScopePolicySetDefinitionsCustom = $grpScopePolicySetDefinitionsCustom.where( { $_.Name -eq 'Sub' }).Group | Sort-Object -Property PolicyDefinitionId | Group-Object ScopeId
-
- $grpScopePolicyAssignments = ($htCacheAssignmentsPolicy).values | Group-Object -Property AssignmentScopeMgSubRg
- $grpMgScopePolicyAssignments = $grpScopePolicyAssignments.where( { $_.Name -eq 'Mg' }).Group | Sort-Object @{Expression = { $_.Assignment.Id } } | Group-Object -Property AssignmentScopeId
- $grpSubScopePolicyAssignments = $grpScopePolicyAssignments.where( { $_.Name -eq 'Sub' }).Group | Sort-Object @{Expression = { $_.Assignment.Id } } | Group-Object -Property AssignmentScopeId
-
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- if (-not $JsonExportExcludeResourceGroups) {
- $grpRGScopePolicyAssignments = $grpScopePolicyAssignments.where( { $_.Name -eq 'RG' }).Group | Sort-Object @{Expression = { $_.Assignment.Id } } | Group-Object -Property AssignmentScopeId
- $htSubRGPolicyAssignments = @{}
- foreach ($rgpa in $grpRGScopePolicyAssignments) {
- $subId = ($rgpa.Name).split('/')[0]
- if (-not $htSubRGPolicyAssignments.($subId)) {
- $htSubRGPolicyAssignments.($subId) = @{}
- }
- if (-not $htSubRGPolicyAssignments.($subId).PolicyAssignments) {
- $htSubRGPolicyAssignments.($subId).PolicyAssignments = @()
- }
- $htSubRGPolicyAssignments.($subId).PolicyAssignments += $rgpa.group
- }
- }
- }
-
- $grpScopeRoleAssignments = ($htCacheAssignmentsRole).values | Group-Object -Property AssignmentScopeTenMgSubRgRes
- $grpTenantScopeRoleAssignments = $grpScopeRoleAssignments.where( { $_.Name -eq 'Tenant' }).Group | Group-Object -Property AssignmentScopeId
- $grpMgScopeRoleAssignments = $grpScopeRoleAssignments.where( { $_.Name -eq 'Mg' }).Group | Sort-Object @{Expression = { $_.Assignment.RoleAssignmentId } } | Group-Object -Property AssignmentScopeId
- $grpSubScopeRoleAssignments = $grpScopeRoleAssignments.where( { $_.Name -eq 'Sub' }).Group | Sort-Object @{Expression = { $_.Assignment.RoleAssignmentId } } | Group-Object -Property AssignmentScopeId
-
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- if (-not $JsonExportExcludeResourceGroups) {
- $grpRGScopeRoleAssignments = $grpScopeRoleAssignments.where( { $_.Name -eq 'RG' }).Group | Sort-Object @{Expression = { $_.Assignment.RoleAssignmentId } } | Group-Object -Property AssignmentScopeId
- $htSubRGRoleAssignments = @{}
- foreach ($rgra in $grpRGScopeRoleAssignments) {
- $subId = ($rgra.Name).split('/')[0]
- if (-not $htSubRGRoleAssignments.($subId)) {
- $htSubRGRoleAssignments.($subId) = @{}
- }
- if (-not $htSubRGRoleAssignments.($subId).RoleAssignments) {
- $htSubRGRoleAssignments.($subId).RoleAssignments = @()
- }
- $htSubRGRoleAssignments.($subId).RoleAssignments += $rgra.group
- }
-
- #res
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- if (-not $JsonExportExcludeResources) {
- $grpResScopeRoleAssignments = $grpScopeRoleAssignments.where( { $_.Name -eq 'Res' }).Group | Sort-Object @{Expression = { $_.Assignment.RoleAssignmentId } } | Group-Object -Property AssignmentScopeId
- $htSubResRoleAssignments = @{}
- foreach ($resra in $grpResScopeRoleAssignments.Group) {
- $raSplit = ($resra.Assignment.RoleAssignmentId).split('/')
- $splitSubId = $raSplit[2]
- $splitRg = $raSplit[4]
- if (-not $htSubResRoleAssignments.($splitSubId)) {
- $htSubResRoleAssignments.($splitSubId) = @{}
- }
- if (-not $htSubResRoleAssignments.($splitSubId).($splitRg)) {
- $htSubResRoleAssignments.($splitSubId).($splitRg) = @{}
-
- }
-
- $resourceName = $resra.AssignmentScopeId.split('/')[2]
- if (-not $htSubResRoleAssignments.($splitSubId).($splitRg).("$($resra.ResourceType)_$($resourceName)")) {
- $htSubResRoleAssignments.($splitSubId).($splitRg).("$($resra.ResourceType)_$($resourceName)") = @{}
-
- }
- if (-not $htSubResRoleAssignments.($splitSubId).($splitRg).("$($resra.ResourceType)_$($resourceName)").RoleAssignments) {
- $htSubResRoleAssignments.($splitSubId).($splitRg).("$($resra.ResourceType)_$($resourceName)").RoleAssignments = [ordered]@{}
-
- }
- ($htSubResRoleAssignments.($splitSubId).($splitRg).("$($resra.ResourceType)_$($resourceName)").RoleAssignments.($resra.Assignment.RoleAssignmentId)) = $resra.Assignment
- }
- }
- }
- }
-
- }
-
- $bluePrintsAssignmentsAtScope = ($htCacheAssignmentsBlueprint).keys | Sort-Object
- $bluePrintDefinitions = ($htCacheDefinitionsBlueprint).Keys | Sort-Object
- $subscriptions = ($optimizedTableForPathQuery.where( { -not [string]::IsNullOrEmpty($_.subscriptionId) })) | Select-Object mgId, Subscription* | Sort-Object -Property subscriptionId -Unique
- foreach ($mg in $MgIds) {
-
- $htJSON.ManagementGroups.($mg.MgId) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).MgId = $mg.MgId
- $htJSON.ManagementGroups.($mg.MgId).MgName = $mg.MgName
- $htJSON.ManagementGroups.($mg.MgId).mgParentId = $mg.mgParentId
- $htJSON.ManagementGroups.($mg.MgId).mgParentName = $mg.mgParentName
- $htJSON.ManagementGroups.($mg.MgId).level = $mg.level
- $htJSON.ManagementGroups.($mg.MgId).PolicyDefinitionsCustom = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).PolicySetDefinitionsCustom = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).BlueprintDefinitions = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).PolicyAssignments = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).RoleAssignments = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).DiagnosticSettings = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions = [ordered]@{}
-
- foreach ($PolDef in (($grpMgScopePolicyDefinitionsCustom).where( { $_.Name -eq $mg.MgId })).group) {
- $htJSON.ManagementGroups.($mg.MgId).PolicyDefinitionsCustom.($PolDef.Id) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).PolicyDefinitionsCustom.($PolDef.Id) = $PolDef.Json
- }
-
- foreach ($PolSetDef in (($grpMgScopePolicySetDefinitionsCustom).where( { $_.Name -eq $mg.MgId })).group) {
- $htJSON.ManagementGroups.($mg.MgId).PolicySetDefinitionsCustom.($PolSetDef.Id) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).PolicySetDefinitionsCustom.($PolSetDef.Id) = $PolSetDef.Json
- }
-
- foreach ($PolAssignment in ($grpMgScopePolicyAssignments).where( { $_.Name -eq $mg.MgId }).group) {
- $htJSON.ManagementGroups.($mg.MgId).PolicyAssignments.($PolAssignment.Assignment.id) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).PolicyAssignments.($PolAssignment.Assignment.id) = $PolAssignment.Assignment
- }
-
- foreach ($RoleAssignment in ($grpMgScopeRoleAssignments).where( { $_.Name -eq $mg.MgId }).group) {
- $htJSON.ManagementGroups.($mg.MgId).RoleAssignments.($RoleAssignment.Assignment.RoleAssignmentId) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).RoleAssignments.($RoleAssignment.Assignment.RoleAssignmentId) = $RoleAssignment.Assignment
- }
-
- foreach ($BlueprintDefinition in ($bluePrintDefinitions).where( { $_ -like "/providers/Microsoft.Management/managementGroups/$($mg.MgId)/*" })) {
- $htJSON.ManagementGroups.($mg.MgId).BlueprintDefinitions.($BlueprintDefinition) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).BlueprintDefinitions.($BlueprintDefinition) = $BlueprintDefinition
- }
-
- if (($htDiagnosticSettingsMgSub).mg.($mg.MgId)) {
- foreach ($entry in ($htDiagnosticSettingsMgSub).mg.($mg.MgId).keys | Sort-Object) {
- $htJSON.ManagementGroups.($mg.MgId).DiagnosticSettings.($entry) = [ordered]@{}
- foreach ($diagset in ($htDiagnosticSettingsMgSub).mg.($mg.MgId).$entry.keys | Sort-Object) {
- $htJSON.ManagementGroups.($mg.MgId).DiagnosticSettings.($entry).Name = (($htDiagnosticSettingsMgSub).mg.($mg.MgId).$entry.$diagset.DiagnosticSettingName)
- $htJSON.ManagementGroups.($mg.MgId).DiagnosticSettings.($entry).Type = (($htDiagnosticSettingsMgSub).mg.($mg.MgId).$entry.$diagset.DiagnosticTargetType)
- $htJSON.ManagementGroups.($mg.MgId).DiagnosticSettings.($entry).TargetId = (($htDiagnosticSettingsMgSub).mg.($mg.MgId).$entry.$diagset.DiagnosticTargetId)
- $htJSON.ManagementGroups.($mg.MgId).DiagnosticSettings.($entry).Settings = (($htDiagnosticSettingsMgSub).mg.($mg.MgId).$entry.$diagset.DiagnosticCategories)
- }
- }
- }
-
- foreach ($subscription in $subscriptions) {
- if ($subscription.MgId -eq $mg.MgId) {
-
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).SubscriptionName = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).SubscriptionQuotaId = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).SubscriptionState = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).SubscriptionTags = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).SubscriptionName = $subscription.Subscription
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).SubscriptionQuotaId = $subscription.SubscriptionQuotaId
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).SubscriptionState = $subscription.SubscriptionState
- if ($htSubscriptionTags.($subscription.SubscriptionId)) {
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).SubscriptionTags = $htSubscriptionTags.($subscription.SubscriptionId).getEnumerator() | Sort-Object Key -CaseSensitive
- }
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).PolicyDefinitionsCustom = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).PolicySetDefinitionsCustom = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).BlueprintDefinitions = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).PolicyAssignments = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).RoleAssignments = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).BlueprintAssignments = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).DiagnosticSettings = [ordered]@{}
-
- foreach ($PolDef in (($grpSubScopePolicyDefinitionsCustom).where( { $_.Name -eq $subscription.subscriptionId })).group) {
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).PolicyDefinitionsCustom.($PolDef.Id) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).PolicyDefinitionsCustom.($PolDef.Id) = $PolDef.Json
- }
-
- foreach ($PolSetDef in (($grpSubScopePolicySetDefinitionsCustom).where( { $_.Name -eq $subscription.subscriptionId })).group) {
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).PolicySetDefinitionsCustom.($PolSetDef.Id) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).PolicySetDefinitionsCustom.($PolSetDef.Id) = $PolSetDef.Json
- }
-
- foreach ($PolAssignment in ($grpSubScopePolicyAssignments).where( { $_.Name -eq $subscription.subscriptionId }).group) {
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).PolicyAssignments.($PolAssignment.Assignment.id) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).PolicyAssignments.($PolAssignment.Assignment.id) = $PolAssignment.Assignment
- }
-
- foreach ($RoleAssignment in ($grpSubScopeRoleAssignments).where( { $_.Name -eq $subscription.subscriptionId }).group) {
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).RoleAssignments.($RoleAssignment.Assignment.RoleAssignmentId) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).RoleAssignments.($RoleAssignment.Assignment.RoleAssignmentId) = $RoleAssignment.Assignment
- }
-
- foreach ($BlueprintDefinition in ($bluePrintDefinitions).where( { $_ -like "/subscriptions/$($subscription.subscriptionId)/*" })) {
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).BlueprintDefinitions.($BlueprintDefinition) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).BlueprintDefinitions.($BlueprintDefinition) = $BlueprintDefinition
- }
-
- foreach ($BlueprintsAssignment in ($blueprintsAssignmentsAtScope).where( { $_ -like "/subscriptions/$($subscription.subscriptionId)/*" })) {
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).BlueprintAssignments.($BlueprintsAssignment) = [ordered]@{}
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).BlueprintAssignments.($BlueprintsAssignment) = $BlueprintsAssignment
- }
-
- if (($htDiagnosticSettingsMgSub).sub.($subscription.subscriptionId)) {
- foreach ($entry in ($htDiagnosticSettingsMgSub).sub.($subscription.subscriptionId).keys | Sort-Object) {
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).DiagnosticSettings.($entry) = [ordered]@{}
- foreach ($diagset in ($htDiagnosticSettingsMgSub).sub.($subscription.subscriptionId).$entry.keys | Sort-Object) {
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).DiagnosticSettings.($entry).Name = (($htDiagnosticSettingsMgSub).sub.($subscription.subscriptionId).$entry.$diagset.DiagnosticSettingName)
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).DiagnosticSettings.($entry).Type = (($htDiagnosticSettingsMgSub).sub.($subscription.subscriptionId).$entry.$diagset.DiagnosticTargetType)
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).DiagnosticSettings.($entry).TargetId = (($htDiagnosticSettingsMgSub).sub.($subscription.subscriptionId).$entry.$diagset.DiagnosticTargetId)
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).DiagnosticSettings.($entry).Settings = (($htDiagnosticSettingsMgSub).sub.($subscription.subscriptionId).$entry.$diagset.DiagnosticCategories)
- }
- }
- }
-
-
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- if (-not $JsonExportExcludeResourceGroups) {
- $htTemp = @{}
- if (-not $htTemp.ResourceGroups) {
- $htTemp.ResourceGroups = @{}
- }
-
- if ($htSubRGPolicyAssignments.($subscription.subscriptionId)) {
- foreach ($rgpa in $htSubRGPolicyAssignments.($subscription.subscriptionId).PolicyAssignments) {
- $rgName = ($rgpa.AssignmentScopeId).split('/')[1]
- if (-not $htTemp.ResourceGroups.($rgName)) {
- $htTemp.ResourceGroups.($rgName) = [ordered]@{}
- }
- if (-not $htTemp.ResourceGroups.($rgName).PolicyAssignments) {
- $htTemp.ResourceGroups.($rgName).PolicyAssignments = [ordered]@{}
- }
- $htTemp.ResourceGroups.($rgName).PolicyAssignments.($rgpa.Assignment.id) = $rgpa.Assignment
- }
- }
- }
- }
-
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- if (-not $JsonExportExcludeResourceGroups) {
- if (-not $htTemp) {
- $htTemp = @{}
- }
- if (-not $htTemp.ResourceGroups) {
- $htTemp.ResourceGroups = @{}
- }
- if ($htSubRGRoleAssignments.($subscription.subscriptionId)) {
- foreach ($rgra in $htSubRGRoleAssignments.($subscription.subscriptionId).RoleAssignments) {
- $rgName = ($rgra.AssignmentScopeId).split('/')[1]
- if (-not $htTemp.ResourceGroups.($rgName)) {
- $htTemp.ResourceGroups.($rgName) = [ordered]@{}
- }
- if (-not $htTemp.ResourceGroups.($rgName).RoleAssignments) {
- $htTemp.ResourceGroups.($rgName).RoleAssignments = [ordered]@{}
- }
- $htTemp.ResourceGroups.($rgName).RoleAssignments.($rgra.Assignment.RoleAssignmentId) = $rgra.Assignment
- }
- }
- #
- if (-not $JsonExportExcludeResources) {
- if (-not $htTemp.ResourceGroups) {
- $htTemp.ResourceGroups = @{}
- }
- if ($htSubResRoleAssignments.($subscription.subscriptionId)) {
- foreach ($rg in $htSubResRoleAssignments.($subscription.subscriptionId).keys) {
- foreach ($res in $htSubResRoleAssignments.($subscription.subscriptionId).($rg).Keys | Sort-Object) {
- $rgName = ($resra.AssignmentScopeId).split('/')[1]
- if (-not $htTemp.ResourceGroups.($rg)) {
- $htTemp.ResourceGroups.($rg) = [ordered]@{}
- }
- if (-not $htTemp.ResourceGroups.($rg).Resources) {
- $htTemp.ResourceGroups.($rg).Resources = [ordered]@{}
- }
- if (-not $htTemp.ResourceGroups.($rg).Resources.($res)) {
- $htTemp.ResourceGroups.($rg).Resources.($res) = [ordered]@{}
- }
- if (-not $htTemp.ResourceGroups.($rg).Resources.($res).RoleAssignments) {
- $htTemp.ResourceGroups.($rg).Resources.($res).RoleAssignments = [ordered]@{}
- }
- $htTemp.ResourceGroups.($rg).Resources.($res).RoleAssignments = $htSubResRoleAssignments.($subscription.subscriptionId).($rg).($res).RoleAssignments
- }
- }
- }
- }
- }
- }
-
- if ($htTemp) {
- $sortedHt = [ordered]@{}
- foreach ($key in ($htTemp.ResourceGroups.keys | Sort-Object)) {
- $sortedHt.($key) = $htTemp.ResourceGroups.($key)
- }
- $htJSON.ManagementGroups.($mg.MgId).Subscriptions.($subscription.subscriptionId).ResourceGroups = $sortedHt
- $htTemp = $null
- $sortedHt = $null
- }
- }
- }
- }
-
- if ($azAPICallConf['htParameters'].onAzureDevOpsOrGitHubActions) {
- if ($ManagementGroupsOnly) {
- $JSONPath = "JSON_ManagementGroupsOnly_$($ManagementGroupId)"
- }
- else {
- $JSONPath = "JSON_$($ManagementGroupId)"
- }
-
- if (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)") {
- Write-Host ' Cleaning old state (Pipeline only)'
- Remove-Item -Recurse -Force "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)"
- }
- }
- else {
- if ($ManagementGroupsOnly) {
- $JSONPath = "JSON_ManagementGroupsOnly_$($ManagementGroupId)_$($fileTimestamp)"
- }
- else {
- $JSONPath = "JSON_$($ManagementGroupId)_$($fileTimestamp)"
- }
- Write-Host " Creating new state ($($JSONPath)) (local only))"
- }
-
- $null = New-Item -Name $JSONPath -ItemType directory -Path $outputPath
-
- if ($azAPICallConf['htParameters'].onAzureDevOpsOrGitHubActions) {
- "The directory '$($JSONPath)' will be rebuilt during the AzDO Pipeline run. __Do not save any files in this directory, files and folders will be deleted!__" | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)$($DirectorySeparatorChar)ReadMe_important.md" -Encoding utf8
- }
-
- $null = New-Item -Name "$($JSONPath)$($DirectorySeparatorChar)Definitions" -ItemType directory -Path $outputPath
-
-
-
-
- $htJSON.RoleDefinitions = [ordered]@{}
- $pathRoleDefinitions = "$($JSONPath)$($DirectorySeparatorChar)Definitions$($DirectorySeparatorChar)RoleDefinitions"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathRoleDefinitions)")) {
- $null = New-Item -Name $pathRoleDefinitions -ItemType directory -Path $outputPath
- $pathRoleDefinitionCustom = "$($pathRoleDefinitions)$($DirectorySeparatorChar)Custom"
- $pathRoleDefinitionBuiltIn = "$($pathRoleDefinitions)$($DirectorySeparatorChar)BuiltIn"
- $null = New-Item -Name "$($pathRoleDefinitionCustom)" -ItemType directory -Path $outputPath
- $null = New-Item -Name "$($pathRoleDefinitionBuiltIn)" -ItemType directory -Path $outputPath
- }
-
- if (($htCacheDefinitionsRole).Keys.Count -gt 0) {
- foreach ($roleDefinition in ($htCacheDefinitionsRole).Keys.where( { ($htCacheDefinitionsRole).($_).IsCustom }) | Sort-Object) {
- $htJSON.RoleDefinitions.($roleDefinition) = ($htCacheDefinitionsRole).($roleDefinition).Json.properties
- $jsonConverted = ($htCacheDefinitionsRole).($roleDefinition).Json.properties | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathRoleDefinitionCustom)$($DirectorySeparatorChar)$(removeInvalidFileNameChars ($htCacheDefinitionsRole).($roleDefinition).Name) ($(($htCacheDefinitionsRole).($roleDefinition).Id)).json" -Encoding utf8
- }
- foreach ($roleDefinition in ($htCacheDefinitionsRole).Keys.where( { -not ($htCacheDefinitionsRole).($_).IsCustom })) {
- $jsonConverted = ($htCacheDefinitionsRole).($roleDefinition).Json | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathRoleDefinitionBuiltIn)$($DirectorySeparatorChar)$(removeInvalidFileNameChars ($htCacheDefinitionsRole).($roleDefinition).Name ) ($(($htCacheDefinitionsRole).($roleDefinition).Id)).json" -Encoding utf8
- }
- }
-
- $pathPolicyDefinitions = "$($JSONPath)$($DirectorySeparatorChar)Definitions$($DirectorySeparatorChar)PolicyDefinitions"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathPolicyDefinitions)")) {
- $null = New-Item -Name $pathPolicyDefinitions -ItemType directory -Path $outputPath
- $pathPolicyDefinitionBuiltIn = "$($pathPolicyDefinitions)$($DirectorySeparatorChar)BuiltIn"
- $null = New-Item -Name "$($pathPolicyDefinitionBuiltIn)" -ItemType directory -Path $outputPath
- }
- if (($htCacheDefinitionsPolicy).Keys.Count -gt 0) {
- foreach ($policyDefinition in ($htCacheDefinitionsPolicy).Keys.where( { ($htCacheDefinitionsPolicy).($_).Type -eq 'BuiltIn' })) {
- $jsonConverted = ($htCacheDefinitionsPolicy).($policyDefinition).Json.properties | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathPolicyDefinitionBuiltIn)$($DirectorySeparatorChar)$(removeInvalidFileNameChars ($htCacheDefinitionsPolicy).($policyDefinition).displayName) ($(($htCacheDefinitionsPolicy).($policyDefinition).Json.name)).json" -Encoding utf8
- }
- }
-
- $pathPolicySetDefinitions = "$($JSONPath)$($DirectorySeparatorChar)Definitions$($DirectorySeparatorChar)PolicySetDefinitions"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathPolicySetDefinitions)")) {
- $null = New-Item -Name $pathPolicySetDefinitions -ItemType directory -Path $outputPath
- $pathPolicySetDefinitionBuiltIn = "$($pathPolicySetDefinitions)$($DirectorySeparatorChar)BuiltIn"
- $null = New-Item -Name "$($pathPolicySetDefinitionBuiltIn)" -ItemType directory -Path $outputPath
- }
- if (($htCacheDefinitionsPolicySet).Keys.Count -gt 0) {
- foreach ($policySetDefinition in ($htCacheDefinitionsPolicySet).Keys.where( { ($htCacheDefinitionsPolicySet).($_).Type -eq 'BuiltIn' })) {
- $jsonConverted = ($htCacheDefinitionsPolicySet).($policySetDefinition).Json.properties | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathPolicySetDefinitionBuiltIn)$($DirectorySeparatorChar)$(removeInvalidFileNameChars ($htCacheDefinitionsPolicySet).($policySetDefinition).displayName) ($(($htCacheDefinitionsPolicySet).($policySetDefinition).Json.name)).json" -Encoding utf8
- }
- }
-
- $endBuildHt = Get-Date
- Write-Host " ht for JSON creation duration: $((New-TimeSpan -Start $startBuildHt -End $endBuildHt).TotalSeconds) seconds"
-
- $startBuildJSON = Get-Date
- Write-Host ' Build JSON'
-
-
- $null = New-Item -Name "$($JSONPath)$($DirectorySeparatorChar)Tenant" -ItemType directory -Path $outputPath
-
- $htTree = [ordered]@{}
- $htTree.'Tenant' = [ordered] @{}
- $htTree.Tenant.TenantId = $azAPICallConf['checkContext'].Tenant.Id
- $htTree.Tenant.RoleAssignments = [ordered]@{}
- foreach ($RoleAssignment in ($grpTenantScopeRoleAssignments).Group | Sort-Object @{Expression = { $_.Assignment.RoleAssignmentId } }) {
-
- $htTree.Tenant.RoleAssignments.$($RoleAssignment.Assignment.RoleAssignmentId) = [ordered]@{}
- $htTree.Tenant.RoleAssignments.$($RoleAssignment.Assignment.RoleAssignmentId) = $RoleAssignment.Assignment
-
- if ($RoleAssignment.Assignment.PIM -eq 'true') {
- $pim = 'PIM_'
- }
- else {
- $pim = ''
- }
- $jsonConverted = ($RoleAssignment.Assignment | Select-Object -ExcludeProperty PIM) | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)$($DirectorySeparatorChar)Tenant$($DirectorySeparatorChar)ra_$($RoleAssignment.Assignment.ObjectType)_$($pim)$($RoleAssignment.Assignment.RoleAssignmentId -replace '.*/').json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Assignments$($DirectorySeparatorChar)RoleAssignments$($DirectorySeparatorChar)Tenant"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($RoleAssignment.Assignment.ObjectType)_$($pim)$($RoleAssignment.Assignment.RoleAssignmentId -replace '.*/').json" -Encoding utf8
- }
-
- $htTree.'Tenant'.'ManagementGroups' = [ordered] @{}
- $json = $htTree.'Tenant'
-
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)$($DirectorySeparatorChar)Assignments")) {
- $null = New-Item -Name "$($JSONPath)$($DirectorySeparatorChar)Assignments" -ItemType directory -Path $outputPath
- }
-
- buildTree -mgId $ManagementGroupId -json $json -prnt "$($JSONPath)$($DirectorySeparatorChar)Tenant"
-
- $htTree.'Tenant'.'CustomRoleDefinitions' = $htJSON.RoleDefinitions
-
- Write-Host " Exporting Tenant JSON '$($outputPath)$($DirectorySeparatorChar)$($JSONPath)$($DirectorySeparatorChar)$($fileName).json'"
- $htTree | ConvertTo-Json -Depth 99 | Set-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)$($DirectorySeparatorChar)$($fileName).json" -Encoding utf8 -Force
-
- $endBuildJSON = Get-Date
- Write-Host " Building JSON duration: $((New-TimeSpan -Start $startBuildJSON -End $endBuildJSON).TotalSeconds) seconds"
-
- $endJSON = Get-Date
- Write-Host "Creating Hierarchy JSON duration: $((New-TimeSpan -Start $startJSON -End $endJSON).TotalSeconds) seconds"
-}
-function buildMD {
- Write-Host 'Building Markdown'
- $startBuildMD = Get-Date
- $script:arrayMgs = [System.Collections.ArrayList]@()
- $script:arraySubs = [System.Collections.ArrayList]@()
- $script:arraySubsOos = [System.Collections.ArrayList]@()
- $markdown = $null
- $script:markdownhierarchyMgs = $null
- $script:markdownhierarchySubs = $null
- $script:markdownTable = $null
-
- if ($azAPICallConf['htParameters'].onAzureDevOpsOrGitHubActions -eq $true) {
- if ($azAPICallConf['htParameters'].onAzureDevOps -eq $true) {
- $markdown += @"
-# Azure Governance Visualizer - Management Group Hierarchy
-
-## HierarchyMap (Mermaid)
-
-::: mermaid
- graph $($MermaidDirection.ToUpper());`n
-"@
- }
- if ($azAPICallConf['htParameters'].onGitHubActions -eq $true) {
- $marks = '```'
- $markdown += @"
-# Azure Governance Visualizer - Management Group Hierarchy
-
-## HierarchyMap (Mermaid)
-
-$($marks)mermaid
- graph $($MermaidDirection.ToUpper());`n
-"@
- }
-
- }
- else {
- $markdown += @"
-# Azure Governance Visualizer - Management Group Hierarchy
-
-$executionDateTimeInternationalReadable ($currentTimeZone)
-
-## HierarchyMap (Mermaid)
-
-::: mermaid
- graph $($MermaidDirection.ToUpper());`n
-"@
- }
-
- processDiagramMermaid
-
- $markdown += @"
-$markdownhierarchyMgs
-$markdownhierarchySubs
- classDef mgr fill:#D9F0FF,stroke:#56595E,color:#000000,stroke-width:1px;
- classDef subs fill:#EEEEEE,stroke:#56595E,color:#000000,stroke-width:1px;
-"@
-
- if (($arraySubsOos).count -gt 0) {
- $markdown += @'
- classDef subsoos fill:#FFCBC7,stroke:#56595E,color:#000000,stroke-width:1px;
-'@
- }
-
- $markdown += @"
- classDef mgrprnts fill:#FFFFFF,stroke:#56595E,color:#000000,stroke-width:1px;
- class $(($arrayMgs | Sort-Object -Unique) -join ',') mgr;
- class $(($arraySubs | Sort-Object -Unique) -join ',') subs;
-"@
-
- if (($arraySubsOos).count -gt 0) {
- $markdown += @"
- class $(($arraySubsOos | Sort-Object -Unique) -join ',') subsoos;
-"@
- }
-
- if ($azAPICallConf['htParameters'].onAzureDevOpsOrGitHubActions -eq $true) {
- if ($azAPICallConf['htParameters'].onAzureDevOps -eq $true) {
- $markdown += @"
-class $mermaidprnts mgrprnts;
-:::
-
-"@
- }
- if ($azAPICallConf['htParameters'].onGitHubActions -eq $true) {
-`
- $marks = '```'
- $markdown += @"
-class $mermaidprnts mgrprnts;
-$marks
-
-"@
- }
- }
- else {
- $markdown += @"
-class $mermaidprnts mgrprnts;
-:::
-
-"@
- }
-
- $markdown += @"
-## Summary
-`n
-"@
- if (-not $HierarchyMapOnly) {
- $markdown += @"
-Total Management Groups: $totalMgCount (depth $mgDepth)\`n
-"@
-
- if (($arraySubsOos).count -gt 0) {
- $markdown += @"
-Total Subscriptions: $totalSubIncludedAndExcludedCount ($totalSubOutOfScopeCount out-of-scope)\`n
-"@
- }
- else {
- $markdown += @"
-Total Subscriptions: $totalSubIncludedAndExcludedCount\`n
-"@
- }
-
- $markdown += @"
-Total Custom Policy definitions: $tenantCustomPoliciesCount\
-Total Custom PolicySet definitions: $tenantCustompolicySetsCount\
-Total Policy assignments: $($totalPolicyAssignmentsCount)\
-Total Policy assignments ManagementGroups $($totalPolicyAssignmentsCountMg)\
-Total Policy assignments Subscriptions $($totalPolicyAssignmentsCountSub)\
-Total Policy assignments ResourceGroups: $($totalPolicyAssignmentsCountRg)\
-Total Custom Role definitions: $totalRoleDefinitionsCustomCount\
-Total Role assignments: $totalRoleAssignmentsCount\
-Total Role assignments (Tenant): $totalRoleAssignmentsCountTen\
-Total Role assignments (ManagementGroups): $totalRoleAssignmentsCountMG\
-Total Role assignments (Subscriptions): $totalRoleAssignmentsCountSub\
-Total Role assignments (ResourceGroups and Resources): $totalRoleAssignmentsResourceGroupsAndResourcesCount\
-Total Blueprint definitions: $totalBlueprintDefinitionsCount\
-Total Blueprint assignments: $totalBlueprintAssignmentsCount\
-Total Resources: $totalResourceCount\
-Total Resource Types: $totalResourceTypesCount
-"@
-
- }
- if ($HierarchyMapOnly) {
- $mgsDetails = ($optimizedTableForPathQueryMg | Select-Object Level, MgId -Unique)
- $mgDepth = ($mgsDetails.Level | Measure-Object -Maximum).Maximum
- $totalMgCount = ($mgsDetails).count
- $totalSubCount = ($optimizedTableForPathQuerySub).count
-
- $markdown += @"
-Total Management Groups: $totalMgCount (depth $mgDepth)\
-Total Subscriptions: $totalSubCount
-"@
-
- }
-
- $markdown += @"
-`n
-## Hierarchy Table
-
-| **MgLevel** | **MgName** | **MgId** | **MgParentName** | **MgParentId** | **SubName** | **SubId** |
-|-------------|-------------|-------------|-------------|-------------|-------------|-------------|
-$markdownTable
-"@
-
- $markdown | Set-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).md" -Encoding utf8 -Force
- $endBuildMD = Get-Date
- Write-Host "Building Markdown total duration: $((New-TimeSpan -Start $startBuildMD -End $endBuildMD).TotalMinutes) minutes ($((New-TimeSpan -Start $startBuildMD -End $endBuildMD).TotalSeconds) seconds)"
-}
-function buildPolicyAllJSON {
- Write-Host 'Creating PolicyAll JSON'
- $startPolicyAllJSON = Get-Date
- $htPolicyAndPolicySet = [ordered]@{}
- $htPolicyAndPolicySet.Policy = [ordered]@{}
- $htPolicyAndPolicySet.PolicySet = [ordered]@{}
- $htPolicyAndPolicySet.PolicyAssignment = [ordered]@{}
- foreach ($policy in ($tenantPoliciesDetailed | Sort-Object -Property Type, ScopeMGLevel, PolicyDefinitionId)) {
- $htPolicyAndPolicySet.Policy.($policy.PolicyDefinitionId.ToLower()) = [ordered]@{
- PolicyType = $policy.Type
- ScopeMGLevel = $policy.ScopeMGLevel
- Scope = $policy.Scope
- ScopeId = $policy.scopeId
- PolicyDisplayName = $policy.PolicyDisplayName
- PolicyDefinitionName = $policy.PolicyDefinitionName
- PolicyDefinitionId = $policy.PolicyDefinitionId
- PolicyEffect = $policy.PolicyEffect
- PolicyCategory = $policy.PolicyCategory
- UniqueAssignmentsCount = $policy.UniqueAssignmentsCount
- UniqueAssignments = $policy.UniqueAssignments
- UsedInPolicySetsCount = $policy.UsedInPolicySetsCount
- UsedInPolicySets = $policy.UsedInPolicySet4JSON
- CreatedOn = $policy.CreatedOn
- CreatedBy = $policy.CreatedByJson
- UpdatedOn = $policy.UpdatedOn
- UpdatedBy = $policy.UpdatedByJson
- JSON = $policy.Json
- }
- }
- foreach ($policySet in ($tenantPolicySetsDetailed | Sort-Object -Property Type, ScopeMGLevel, PolicySetDefinitionId)) {
- $htPolicyAndPolicySet.PolicySet.($policySet.PolicySetDefinitionId.ToLower()) = [ordered]@{
- PolicySetType = $policySet.Type
- ScopeMGLevel = $policySet.ScopeMGLevel
- Scope = $policySet.Scope
- ScopeId = $policySet.scopeId
- PolicySetDisplayName = $policySet.PolicySetDisplayName
- PolicySetDefinitionName = $policySet.PolicySetDefinitionName
- PolicySetDefinitionId = $policySet.PolicySetDefinitionId
- PolicySetCategory = $policySet.PolicySetCategory
- UniqueAssignmentsCount = $policySet.UniqueAssignmentsCount
- UniqueAssignments = $policySet.UniqueAssignments
- PoliciesUsedCount = $policySet.PoliciesUsedCount
- PoliciesUsed = $policySet.PoliciesUsed4JSON
- CreatedOn = $policySet.CreatedOn
- CreatedBy = $policySet.CreatedByJson
- UpdatedOn = $policySet.UpdatedOn
- UpdatedBy = $policySet.UpdatedByJson
- JSON = $policySet.Json
- }
- }
- foreach ($key in $htCacheAssignmentsPolicy.keys | Sort-Object) {
- $htPolicyAndPolicySet.PolicyAssignment.($key.ToLower()) = $htCacheAssignmentsPolicy.($key).Assignment
- }
- Write-Host " Exporting PolicyAll JSON '$($outputPath)$($DirectorySeparatorChar)$($fileName)_PolicyAll.json'"
- $htPolicyAndPolicySet | ConvertTo-Json -Depth 99 | Set-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_PolicyAll.json" -Encoding utf8 -Force
-
- $endPolicyAllJSON = Get-Date
- Write-Host "Creating PolicyAll JSON duration: $((New-TimeSpan -Start $startPolicyAllJSON -End $endPolicyAllJSON).TotalSeconds) seconds"
-}
-function buildTree($mgId, $prnt) {
- $getMg = $htEntities.values.where( { $_.type -eq 'Microsoft.Management/managementGroups' -and $_.id -eq $mgId })
- $childrenManagementGroups = $htEntities.values.where( { $_.type -eq 'Microsoft.Management/managementGroups' -and $_.parentId -eq "/providers/Microsoft.Management/managementGroups/$($getMg.Id)" })
- $mgNameValid = removeInvalidFileNameChars $getMg.Id
- $mgDisplayNameValid = removeInvalidFileNameChars $getMg.displayName
- $prntx = "$($prnt)$($DirectorySeparatorChar)$($mgNameValid) ($($mgDisplayNameValid))"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($prntx)")) {
- $null = New-Item -Name $prntx -ItemType directory -Path $outputPath
- }
-
- if (-not $json.'ManagementGroups') {
- $json.'ManagementGroups' = [ordered]@{}
- }
- $json = $json.'ManagementGroups'.($getMg.Id) = [ordered]@{}
- foreach ($mgCap in $htJSON.ManagementGroups.($getMg.Id).keys) {
- $json.$mgCap = $htJSON.ManagementGroups.($getMg.Id).$mgCap
- if ($mgCap -eq 'PolicyDefinitionsCustom') {
- $mgCapShort = 'pd'
- foreach ($pdc in $htJSON.ManagementGroups.($getMg.Id).($mgCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($pdc)
- if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
- $displayName = 'noDisplayNameGiven'
- }
- else {
- $displayName = removeInvalidFileNameChars $hlp.properties.displayName
- }
- $jsonConverted = $hlp.properties | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($prntx)$($DirectorySeparatorChar)$($mgCapShort)_$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Definitions$($DirectorySeparatorChar)PolicyDefinitions$($DirectorySeparatorChar)Custom$($DirectorySeparatorChar)Mg$($DirectorySeparatorChar)$($mgNameValid) ($($mgDisplayNameValid))"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- }
- }
- if ($mgCap -eq 'PolicySetDefinitionsCustom') {
- $mgCapShort = 'psd'
- foreach ($psdc in $htJSON.ManagementGroups.($getMg.Id).($mgCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($psdc)
- if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
- $displayName = 'noDisplayNameGiven'
- }
- else {
- $displayName = removeInvalidFileNameChars $hlp.properties.displayName
- }
- $jsonConverted = $hlp.properties | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($prntx)$($DirectorySeparatorChar)$($mgCapShort)_$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Definitions$($DirectorySeparatorChar)PolicySetDefinitions$($DirectorySeparatorChar)Custom$($DirectorySeparatorChar)Mg$($DirectorySeparatorChar)$($mgNameValid) ($($mgDisplayNameValid))"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- }
- }
- if ($mgCap -eq 'PolicyAssignments') {
- $mgCapShort = 'pa'
- foreach ($pa in $htJSON.ManagementGroups.($getMg.Id).($mgCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($pa)
- if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
- $displayName = 'noDisplayNameGiven'
- }
- else {
- $displayName = removeInvalidFileNameChars $hlp.properties.displayName
- }
- $jsonConverted = $hlp | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($prntx)$($DirectorySeparatorChar)$($mgCapShort)_$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Assignments$($DirectorySeparatorChar)$($mgCap)$($DirectorySeparatorChar)Mg$($DirectorySeparatorChar)$($mgNameValid) ($($mgDisplayNameValid))"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
-
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- }
- }
- #marker
- if ($mgCap -eq 'RoleAssignments') {
- $mgCapShort = 'ra'
- foreach ($ra in $htJSON.ManagementGroups.($getMg.Id).($mgCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($ra)
- if ($hlp.PIM -eq 'true') {
- $pim = 'PIM_'
- }
- else {
- $pim = ''
- }
- $jsonConverted = ($hlp | Select-Object -ExcludeProperty PIM) | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($prntx)$($DirectorySeparatorChar)$($mgCapShort)_$($hlp.ObjectType)_$($pim)$($hlp.RoleAssignmentId -replace '.*/').json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Assignments$($DirectorySeparatorChar)$($mgCap)$($DirectorySeparatorChar)Mg$($DirectorySeparatorChar)$($mgNameValid) ($($mgDisplayNameValid))"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($hlp.ObjectType)_$($pim)$($hlp.RoleAssignmentId -replace '.*/').json" -Encoding utf8
- }
- }
-
- if ($mgCap -eq 'Subscriptions') {
- foreach ($sub in $htJSON.ManagementGroups.($getMg.Id).($mgCap).Keys) {
- $subNameValid = removeInvalidFileNameChars $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).SubscriptionName
- $subFolderName = "$($prntx)$($DirectorySeparatorChar)$($subNameValid) ($($sub))"
- $null = New-Item -Name $subFolderName -ItemType directory -Path $outputPath
- foreach ($subCap in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).Keys) {
- if ($subCap -eq 'PolicyDefinitionsCustom') {
- $subCapShort = 'pd'
- foreach ($pdc in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($pdc)
- if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
- $displayName = 'noDisplayNameGiven'
- }
- else {
- $displayName = removeInvalidFileNameChars $hlp.properties.displayName
- }
- $jsonConverted = $hlp.properties | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($subCapShort)_$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Definitions$($DirectorySeparatorChar)PolicyDefinitions$($DirectorySeparatorChar)Custom$($DirectorySeparatorChar)Sub$($DirectorySeparatorChar)$($subNameValid) ($($sub))"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- }
- }
- if ($subCap -eq 'PolicySetDefinitionsCustom') {
- $subCapShort = 'psd'
- foreach ($psdc in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($psdc)
- if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
- $displayName = 'noDisplayNameGiven'
- }
- else {
- $displayName = removeInvalidFileNameChars $hlp.properties.displayName
- }
- $jsonConverted = $hlp.properties | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($subCapShort)_$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Definitions$($DirectorySeparatorChar)PolicySetDefinitions$($DirectorySeparatorChar)Custom$($DirectorySeparatorChar)Sub$($DirectorySeparatorChar)$($subNameValid) ($($sub))"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- }
- }
- if ($subCap -eq 'PolicyAssignments') {
- $subCapShort = 'pa'
- foreach ($pa in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($pa)
- if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
- $displayName = 'noDisplayNameGiven'
- }
- else {
- $displayName = removeInvalidFileNameChars $hlp.properties.displayName
- }
- $jsonConverted = $hlp | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($subCapShort)_$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Assignments$($DirectorySeparatorChar)$($subCap)$($DirectorySeparatorChar)Sub$($DirectorySeparatorChar)$($subNameValid) ($($sub))"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
- }
- }
- #marker
- if ($subCap -eq 'RoleAssignments') {
- $subCapShort = 'ra'
- foreach ($ra in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($ra)
- if ($hlp.PIM -eq 'true') {
- $pim = 'PIM_'
- }
- else {
- $pim = ''
- }
- $jsonConverted = ($hlp | Select-Object -ExcludeProperty PIM) | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($subCapShort)_$($pim)$($hlp.ObjectType)_$($hlp.RoleAssignmentId -replace '.*/').json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Assignments$($DirectorySeparatorChar)$($subCap)$($DirectorySeparatorChar)Sub$($DirectorySeparatorChar)$($subNameValid) ($($sub))"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($hlp.ObjectType)_$($pim)$($hlp.RoleAssignmentId -replace '.*/').json" -Encoding utf8
- }
- }
-
- #RG Pol
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- if (-not $JsonExportExcludeResourceGroups) {
- if ($subCap -eq 'ResourceGroups') {
- foreach ($rg in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys | Sort-Object) {
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($rg)")) {
- $null = New-Item -Name "$($subFolderName)$($DirectorySeparatorChar)$($rg)" -ItemType directory -Path "$($outputPath)"
- }
- foreach ($pa in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).PolicyAssignments.keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).PolicyAssignments.($pa)
- if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
- $displayName = 'noDisplayNameGiven'
- }
- else {
- $displayName = removeInvalidFileNameChars $hlp.properties.displayName
- }
- $jsonConverted = $hlp | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($rg)$($DirectorySeparatorChar)pa_$($displayName) ($($hlp.name)).json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Assignments$($DirectorySeparatorChar)PolicyAssignments$($DirectorySeparatorChar)Sub$($DirectorySeparatorChar)$($subNameValid) ($($sub))$($DirectorySeparatorChar)$($rg)"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($($hlp.name)).json" -Encoding utf8
- }
- }
- }
- }
- }
-
- #RG RoleAss
- #marker
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- if (-not $JsonExportExcludeResourceGroups) {
- if ($subCap -eq 'ResourceGroups') {
- foreach ($rg in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys | Sort-Object) {
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($rg)")) {
- $null = New-Item -Name "$($subFolderName)$($DirectorySeparatorChar)$($rg)" -ItemType directory -Path "$($outputPath)"
- }
- foreach ($ra in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).RoleAssignments.keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).RoleAssignments.($ra)
- if ($hlp.PIM -eq 'true') {
- $pim = 'PIM_'
- }
- else {
- $pim = ''
- }
- $jsonConverted = ($hlp | Select-Object -ExcludeProperty PIM) | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($rg)$($DirectorySeparatorChar)ra_$($hlp.ObjectType)_$($pim)$($hlp.RoleAssignmentId -replace '.*/').json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Assignments$($DirectorySeparatorChar)RoleAssignments$($DirectorySeparatorChar)Sub$($DirectorySeparatorChar)$($subNameValid) ($($sub))$($DirectorySeparatorChar)$($rg)"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($hlp.ObjectType)_$($pim)$($hlp.RoleAssignmentId -replace '.*/').json" -Encoding utf8
- }
- #res
- if (-not $JsonExportExcludeResources) {
-
- foreach ($res in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).Resources.keys) {
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($rg)$($DirectorySeparatorChar)$($res)")) {
- $null = New-Item -Name "$($subFolderName)$($DirectorySeparatorChar)$($rg)$($DirectorySeparatorChar)$($res)" -ItemType directory -Path "$($outputPath)"
- }
- foreach ($ra in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).Resources.($res).RoleAssignments.keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).Resources.($res).RoleAssignments.($ra)
- if ($hlp.PIM -eq 'true') {
- $pim = 'PIM_'
- }
- else {
- $pim = ''
- }
- $jsonConverted = ($hlp | Select-Object -ExcludeProperty PIM) | ConvertTo-Json -Depth 99
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($rg)$($DirectorySeparatorChar)$($res)$($DirectorySeparatorChar)ra_$($hlp.ObjectType)_$($pim)$($hlp.RoleAssignmentId -replace '.*/').json" -Encoding utf8
- $path = "$($JSONPath)$($DirectorySeparatorChar)Assignments$($DirectorySeparatorChar)RoleAssignments$($DirectorySeparatorChar)Sub$($DirectorySeparatorChar)$($subNameValid) ($($sub))$($DirectorySeparatorChar)$($rg)$($DirectorySeparatorChar)$($res)"
- if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
- $null = New-Item -Name $path -ItemType directory -Path $outputPath
- }
- $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($hlp.ObjectType)_$($pim)$($hlp.RoleAssignmentId -replace '.*/').json" -Encoding utf8
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- if ($childrenManagementGroups.Count -eq 0) {
- $json.'ManagementGroups' = @{}
- }
- else {
- foreach ($childMg in $childrenManagementGroups | Sort-Object -Property Id) {
- buildTree -mgId $childMg.Id -json $json -prnt $prntx
- }
- }
-}
-function cacheBuiltIn {
- $startDefinitionsCaching = Get-Date
- Write-Host 'Caching built-in Policy and RBAC Role definitions'
-
- $arrayBuiltInCaching = @('PolicyDefinitions', 'PolicyDefinitionsStatic', 'PolicySetDefinitions', 'RoleDefinitions')
-
- $arrayBuiltInCaching | ForEach-Object -Parallel {
-
- $builtInCapability = $_
- #fromOtherFunctions
- $azAPICallConf = $using:azAPICallConf
- $scriptPath = $using:ScriptPath
- #Array&HTs
- $htCacheDefinitionsPolicy = $using:htCacheDefinitionsPolicy
- $htCacheDefinitionsPolicySet = $using:htCacheDefinitionsPolicySet
- $htCacheDefinitionsRole = $using:htCacheDefinitionsRole
- $htRoleDefinitionIdsUsedInPolicy = $using:htRoleDefinitionIdsUsedInPolicy
- $ValidPolicyEffects = $using:ValidPolicyEffects
- $htHashesBuiltInPolicy = $using:htHashesBuiltInPolicy
- #vars
- $ARMLocation = $using:ARMLocation
- $ignoreARMLocation = $using:ignoreARMLocation
- #functions
- $function:detectPolicyEffect = $using:funcDetectPolicyEffect
- $function:getPolicyHash = $using:funcGetPolicyHash
-
- if ($builtInCapability -eq 'PolicyDefinitions') {
- $currentTask = 'Caching built-in Policy definitions'
- Write-Host " $currentTask"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Authorization/policyDefinitions?api-version=2021-06-01&`$filter=policyType eq 'BuiltIn'"
- $method = 'GET'
- $requestPolicyDefinitionAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- Write-Host " $($requestPolicyDefinitionAPI.Count) built-in Policy definitions returned"
- $builtinPolicyDefinitions = $requestPolicyDefinitionAPI.where( { $_.properties.policyType -eq 'BuiltIn' } )
-
- foreach ($builtinPolicyDefinition in $builtinPolicyDefinitions) {
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()) = @{}
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Id = ($builtinPolicyDefinition.Id).ToLower()
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).ScopeMGLevel = ''
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Scope = 'n/a'
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).ScopeMgSub = 'n/a'
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).ScopeId = 'n/a'
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).DisplayName = $builtinPolicyDefinition.Properties.displayname
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Name = $builtinPolicyDefinition.Name
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Description = $builtinPolicyDefinition.Properties.description
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Type = $builtinPolicyDefinition.Properties.policyType
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Category = $builtinPolicyDefinition.Properties.metadata.category
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Version = $builtinPolicyDefinition.Properties.metadata.version
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).PolicyDefinitionId = ($builtinPolicyDefinition.Id).ToLower()
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).LinkToAzAdvertizer = "$($builtinPolicyDefinition.Properties.displayname) "
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).ALZ = $false
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).ALZState = ''
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).ALZLatestVer = ''
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).ALZIdentificationLevel = ''
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).ALZPolicyName = ''
- if ($builtinPolicyDefinition.Properties.metadata.deprecated -eq $true -or $builtinPolicyDefinition.Properties.displayname -like "``[Deprecated``]*") {
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Deprecated = $builtinPolicyDefinition.Properties.metadata.deprecated
- }
- else {
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Deprecated = $false
- }
- if ($builtinPolicyDefinition.Properties.metadata.preview -eq $true -or $builtinPolicyDefinition.Properties.displayname -like "``[*Preview``]*") {
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Preview = $builtinPolicyDefinition.Properties.metadata.preview
- }
- else {
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Preview = $false
- }
- #region effect
- $htEffectDetected = detectPolicyEffect -policyDefinition $builtinPolicyDefinition
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).effectDefaultValue = $htEffectDetected.defaultValue
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).effectAllowedValue = $htEffectDetected.allowedValues
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).effectFixedValue = $htEffectDetected.fixedValue
- #endregion effect
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).Json = $builtinPolicyDefinition
-
- if (-not [string]::IsNullOrWhiteSpace($builtinPolicyDefinition.properties.policyRule.then.details.roleDefinitionIds)) {
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).RoleDefinitionIds = $builtinPolicyDefinition.properties.policyRule.then.details.roleDefinitionIds
- foreach ($roledefinitionId in $builtinPolicyDefinition.properties.policyRule.then.details.roleDefinitionIds) {
- if (-not $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId)) {
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId) = @{}
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [array]$builtinPolicyDefinition.Id
- }
- else {
- $usedInPolicies = $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies
- $usedInPolicies += $builtinPolicyDefinition.Id
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = $usedInPolicies
- }
- }
- }
- else {
- $script:htCacheDefinitionsPolicy.(($builtinPolicyDefinition.Id).ToLower()).RoleDefinitionIds = 'n/a'
- }
-
- #hashes for parity builtin/custom
- # $script:htHashesBuiltInPolicy.(($builtinPolicyDefinition.Id).ToLower()) = @{
- # policyRuleHash = getPolicyHash -object ($builtinPolicyDefinition.properties.policyRule | ConvertTo-Json -Depth 99)
- # }
- $policyRuleHash = (getPolicyHash -json ($builtinPolicyDefinition.properties.policyRule | ConvertTo-Json -Depth 99))
- if (-not $htHashesBuiltInPolicy.($policyRuleHash)) {
- $script:htHashesBuiltInPolicy.($policyRuleHash) = @{
- Policies = [System.Collections.ArrayList]@()
- }
- $null = $script:htHashesBuiltInPolicy.($policyRuleHash).Policies.Add(($builtinPolicyDefinition.Id).ToLower())
- }
- else {
- #Write-Host "$($builtinPolicyDefinition.name) $($policyRuleHash) already exists"
- $null = $script:htHashesBuiltInPolicy.($policyRuleHash).Policies.Add(($builtinPolicyDefinition.Id).ToLower())
- #$htHashesBuiltInPolicy.($policyRuleHash).Policies.Count
- }
- }
- Write-Host " $($htHashesBuiltInPolicy.Keys.Count) unique Policy rule hashes for built-in Policy definitions"
- }
-
- if ($builtInCapability -eq 'PolicyDefinitionsStatic') {
- $currentTask = 'Caching static Policy definitions'
- Write-Host " $currentTask"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Authorization/policyDefinitions?api-version=2021-06-01&`$filter=policyType eq 'Static'"
- $method = 'GET'
- $requestPolicyDefinitionAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- Write-Host " $($requestPolicyDefinitionAPI.Count) static Policy definitions returned"
- $staticPolicyDefinitions = $requestPolicyDefinitionAPI.where( { $_.properties.policyType -eq 'Static' } )
-
- foreach ($staticPolicyDefinition in $staticPolicyDefinitions) {
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()) = @{}
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Id = ($staticPolicyDefinition.Id).ToLower()
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).ScopeMGLevel = ''
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Scope = 'n/a'
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).ScopeMgSub = 'n/a'
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).ScopeId = 'n/a'
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).DisplayName = $staticPolicyDefinition.Properties.displayname
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Name = $staticPolicyDefinition.Name
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Description = $staticPolicyDefinition.Properties.description
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Type = $staticPolicyDefinition.Properties.policyType
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Category = $staticPolicyDefinition.Properties.metadata.category
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Version = $staticPolicyDefinition.Properties.metadata.version
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).PolicyDefinitionId = ($staticPolicyDefinition.Id).ToLower()
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).LinkToAzAdvertizer = "$($staticPolicyDefinition.Properties.displayname) "
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).ALZ = $false
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).ALZState = ''
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).ALZLatestVer = ''
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).ALZIdentificationLevel = ''
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).ALZPolicyName = ''
-
- if ($staticPolicyDefinition.Properties.metadata.deprecated -eq $true -or $staticPolicyDefinition.Properties.displayname -like "``[Deprecated``]*") {
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Deprecated = $staticPolicyDefinition.Properties.metadata.deprecated
- }
- else {
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Deprecated = $false
- }
- if ($staticPolicyDefinition.Properties.metadata.preview -eq $true -or $staticPolicyDefinition.Properties.displayname -like "``[*Preview``]*") {
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Preview = $staticPolicyDefinition.Properties.metadata.preview
- }
- else {
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Preview = $false
- }
- #region effect
- $htEffectDetected = detectPolicyEffect -policyDefinition $staticPolicyDefinition
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).effectDefaultValue = $htEffectDetected.defaultValue
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).effectAllowedValue = $htEffectDetected.allowedValues
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).effectFixedValue = $htEffectDetected.fixedValue
- #endregion effect
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).Json = $staticPolicyDefinition
-
- if (-not [string]::IsNullOrWhiteSpace($staticPolicyDefinition.properties.policyRule.then.details.roleDefinitionIds)) {
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).RoleDefinitionIds = $staticPolicyDefinition.properties.policyRule.then.details.roleDefinitionIds
- foreach ($roledefinitionId in $staticPolicyDefinition.properties.policyRule.then.details.roleDefinitionIds) {
- if (-not $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId)) {
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId) = @{}
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [array]$staticPolicyDefinition.Id
- }
- else {
- $usedInPolicies = $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies
- $usedInPolicies += $staticPolicyDefinition.Id
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = $usedInPolicies
- }
- }
- }
- else {
- $script:htCacheDefinitionsPolicy.(($staticPolicyDefinition.Id).ToLower()).RoleDefinitionIds = 'n/a'
- }
- }
- }
-
- if ($builtInCapability -eq 'PolicySetDefinitions') {
-
- $currentTask = 'Caching built-in PolicySet definitions'
- Write-Host " $currentTask"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Authorization/policySetDefinitions?api-version=2021-06-01&`$filter=policyType eq 'BuiltIn'"
- $method = 'GET'
- $requestPolicySetDefinitionAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- $builtinPolicySetDefinitions = $requestPolicySetDefinitionAPI.where( { $_.properties.policyType -eq 'BuiltIn' } )
- Write-Host " $($requestPolicySetDefinitionAPI.Count) built-in PolicySet definitions returned"
- foreach ($builtinPolicySetDefinition in $builtinPolicySetDefinitions) {
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()) = @{}
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Id = ($builtinPolicySetDefinition.Id).ToLower()
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).ScopeMGLevel = ''
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Scope = 'n/a'
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).ScopeMgSub = 'n/a'
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).ScopeId = 'n/a'
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).DisplayName = $builtinPolicySetDefinition.Properties.displayname
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Name = $builtinPolicySetDefinition.Name
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Description = $builtinPolicySetDefinition.Properties.description
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Type = $builtinPolicySetDefinition.Properties.policyType
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Category = $builtinPolicySetDefinition.Properties.metadata.category
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Version = $builtinPolicySetDefinition.Properties.metadata.version
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).PolicyDefinitionId = ($builtinPolicySetDefinition.Id).ToLower()
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).LinkToAzAdvertizer = "$($builtinPolicySetDefinition.Properties.displayname) "
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).ALZ = $false
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).ALZState = ''
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).ALZLatestVer = ''
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).ALZIdentificationLevel = ''
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).ALZPolicySetName = ''
- $arrayPolicySetPolicyIdsToLower = @()
- $htPolicySetPolicyRefIds = @{}
- $arrayPolicySetPolicyIdsToLower = foreach ($policySetPolicy in $builtinPolicySetDefinition.properties.policydefinitions) {
- ($policySetPolicy.policyDefinitionId).ToLower()
- $htPolicySetPolicyRefIds.($policySetPolicy.policyDefinitionReferenceId) = ($policySetPolicy.policyDefinitionId)
- }
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).PolicySetPolicyIds = $arrayPolicySetPolicyIdsToLower
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).PolicySetPolicyRefIds = $htPolicySetPolicyRefIds
- if ($builtinPolicySetDefinition.Properties.metadata.deprecated -eq $true -or $builtinPolicySetDefinition.Properties.displayname -like "``[Deprecated``]*") {
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Deprecated = $builtinPolicySetDefinition.Properties.metadata.deprecated
- }
- else {
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Deprecated = $false
- }
- if ($builtinPolicySetDefinition.Properties.metadata.preview -eq $true -or $builtinPolicySetDefinition.Properties.displayname -like "``[*Preview``]*") {
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Preview = $builtinPolicySetDefinition.Properties.metadata.preview
- }
- else {
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Preview = $false
- }
- $script:htCacheDefinitionsPolicySet.(($builtinPolicySetDefinition.Id).ToLower()).Json = $builtinPolicySetDefinition
- }
- }
-
- if ($builtInCapability -eq 'RoleDefinitions') {
- #Write-Host "`$ignoreARMLocation = '$ignoreARMLocation'" -ForegroundColor Yellow
- if ($ignoreARMLocation) {
- $currentTask = 'Caching built-in Role definitions'
- Write-Host " $currentTask"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].'ARM')/subscriptions/$($azAPICallConf['checkContext'].Subscription.Id)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'BuiltInRole'"
- #$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'BuiltInRole'"
- }
- else {
- $currentTask = "Caching built-in Role definitions (Location: '$($ARMLocation)')"
- Write-Host " $currentTask"
- $uri = "$($azAPICallConf['azAPIEndpointUrls']."ARM$($ARMLocation)")/subscriptions/$($azAPICallConf['checkContext'].Subscription.Id)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'BuiltInRole'"
- #$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'BuiltInRole'"
- }
-
- $method = 'GET'
- $requestRoleDefinitionAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- Write-Host " $($requestRoleDefinitionAPI.Count) built-in Role definitions returned"
- foreach ($roleDefinition in $requestRoleDefinitionAPI) {
- if (
- (
- $roleDefinition.properties.permissions.actions -contains 'Microsoft.Authorization/roleassignments/write' -or
- $roleDefinition.properties.permissions.actions -contains 'Microsoft.Authorization/roleassignments/*' -or
- $roleDefinition.properties.permissions.actions -contains 'Microsoft.Authorization/*/write' -or
- $roleDefinition.properties.permissions.actions -contains 'Microsoft.Authorization/*' -or
- $roleDefinition.properties.permissions.actions -contains '*/write' -or
- $roleDefinition.properties.permissions.actions -contains '*'
- ) -and (
- $roleDefinition.properties.permissions.notActions -notcontains 'Microsoft.Authorization/roleassignments/write' -and
- $roleDefinition.properties.permissions.notActions -notcontains 'Microsoft.Authorization/roleassignments/*' -and
- $roleDefinition.properties.permissions.notActions -notcontains 'Microsoft.Authorization/*/write' -and
- $roleDefinition.properties.permissions.notActions -notcontains 'Microsoft.Authorization/*' -and
- $roleDefinition.properties.permissions.notActions -notcontains '*/write' -and
- $roleDefinition.properties.permissions.notActions -notcontains '*'
- )
- ) {
- $roleCapable4RoleAssignmentsWrite = $true
- }
- else {
- $roleCapable4RoleAssignmentsWrite = $false
- }
-
- ($script:htCacheDefinitionsRole).($roleDefinition.name) = @{}
- ($script:htCacheDefinitionsRole).($roleDefinition.name).Id = ($roleDefinition.name)
- ($script:htCacheDefinitionsRole).($roleDefinition.name).Name = ($roleDefinition.properties.roleName)
- ($script:htCacheDefinitionsRole).($roleDefinition.name).IsCustom = $false
- ($script:htCacheDefinitionsRole).($roleDefinition.name).AssignableScopes = ($roleDefinition.properties.assignableScopes)
- ($script:htCacheDefinitionsRole).($roleDefinition.name).Actions = ($roleDefinition.properties.permissions.actions)
- ($script:htCacheDefinitionsRole).($roleDefinition.name).NotActions = ($roleDefinition.properties.permissions.notActions)
- ($script:htCacheDefinitionsRole).($roleDefinition.name).DataActions = ($roleDefinition.properties.permissions.dataActions)
- ($script:htCacheDefinitionsRole).($roleDefinition.name).NotDataActions = ($roleDefinition.properties.permissions.notDataActions)
- ($script:htCacheDefinitionsRole).($roleDefinition.name).Json = ($roleDefinition.properties)
- ($script:htCacheDefinitionsRole).($roleDefinition.name).LinkToAzAdvertizer = "$($roleDefinition.properties.roleName) "
- ($script:htCacheDefinitionsRole).($roleDefinition.name).RoleCanDoRoleAssignments = $roleCapable4RoleAssignmentsWrite
- }
- }
- }
-
- $script:builtInPolicyDefinitionsCount = $htCacheDefinitionsPolicy.Values.where({ $_.Type -eq 'BuiltIn' }).count
-
- $endDefinitionsCaching = Get-Date
- Write-Host "Caching built-in definitions duration: $((New-TimeSpan -Start $startDefinitionsCaching -End $endDefinitionsCaching).TotalSeconds) seconds"
-}
-function checkAzGovVizVersion {
- try {
- $getRepoVersion = Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/master/version.json'
- $repoVersion = ($getRepoVersion.Content | ConvertFrom-Json).ProductVersion
-
- $script:azGovVizNewerVersionAvailable = $false
- if ($repoVersion -ne $ProductVersion) {
- $repoVersionSplit = $repoVersion -split '\.'
- $repoVersionMajor = $repoVersionSplit[0]
- $repoVersionMinor = $repoVersionSplit[1]
- $repoVersionPatch = $repoVersionSplit[2]
-
- $ProductVersionSplit = $ProductVersion -split '\.'
- $ProductVersionMajor = $ProductVersionSplit[0]
- $ProductVersionMinor = $ProductVersionSplit[1]
- $ProductVersionPatch = $ProductVersionSplit[2]
-
- if ($repoVersionMajor -ne $ProductVersionMajor) {
- $versionDrift = 'major'
- }
- elseif ($repoVersionMinor -ne $ProductVersionMinor) {
- $versionDrift = 'minor'
- }
- elseif ($repoVersionPatch -ne $ProductVersionPatch) {
- $versionDrift = 'patch'
- }
- else {
- $versionDrift = 'unknown'
- }
-
- $versionDriftSummary = "$repoVersion ($versionDrift)"
- $script:azGovVizVersionOnRepositoryFull = $versionDriftSummary
- $script:azGovVizNewerVersionAvailable = $true
- $script:azGovVizNewerVersionAvailableHTML = 'Get the latest Azure Governance Visualizer version ' + $azGovVizVersionOnRepositoryFull + '! '
- }
- else {
- Write-Host "Azure Governance Visualizer version is up to date '$ProductVersion'" -ForegroundColor Green
- }
- }
- catch {
- #skip
- Write-Host 'Azure Governance Visualizer version check skipped' -ForegroundColor Magenta
- }
-}
-function createTagList {
- $startTagListArray = Get-Date
- Write-Host 'Creating TagList array'
-
- $tagsSubRgResCount = ($htAllTagList.'AllScopes'.Keys).Count
- $tagsSubsriptionCount = ($htAllTagList.'Subscription'.Keys).Count
- $tagsResourceGroupCount = ($htAllTagList.'ResourceGroup'.Keys).Count
- $tagsResourceCount = ($htAllTagList.'Resource'.Keys).Count
- Write-Host " Total Number of ALL unique Tag Names: $tagsSubRgResCount"
- Write-Host " Total Number of Subscription unique Tag Names: $tagsSubsriptionCount"
- Write-Host " Total Number of ResourceGroup unique Tag Names: $tagsResourceGroupCount"
- Write-Host " Total Number of Resource unique Tag Names: $tagsResourceCount"
-
- foreach ($tagScope in $htAllTagList.keys) {
- foreach ($tagScopeTagName in $htAllTagList.($tagScope).keys) {
- $null = $script:arrayTagList.Add([PSCustomObject]@{
- Scope = $tagScope
- TagName = ($tagScopeTagName)
- TagCount = $htAllTagList.($tagScope).($tagScopeTagName)
- })
- }
- }
- $endTagListArray = Get-Date
- Write-Host "Creating TagList array duration: $((New-TimeSpan -Start $startTagListArray -End $endTagListArray).TotalMinutes) minutes ($((New-TimeSpan -Start $startTagListArray -End $endTagListArray).TotalSeconds) seconds)"
-}
-function detailSubscriptions {
- $start = Get-Date
- Write-Host 'Subscription picking'
- #API in rare cases returns duplicates, therefor sorting unique (id)
- $childrenSubscriptions = $arrayEntitiesFromAPI.where( { $_.properties.parentNameChain -contains $ManagementGroupID -and $_.type -eq '/subscriptions' } ) | Sort-Object -Property id -Unique
- $script:childrenSubscriptionsCount = ($childrenSubscriptions).Count
- $script:subsToProcessInCustomDataCollection = [System.Collections.ArrayList]@()
-
- if ($htSubscriptionsFromOtherTenants.keys.count -gt 0) {
- foreach ($subscriptionExludedOtherTenant in $htSubscriptionsFromOtherTenants.keys) {
- $subscriptionExludedOtherTenantDetail = $htSubscriptionsFromOtherTenants.($subscriptionExludedOtherTenant).subDetails
- $null = $script:outOfScopeSubscriptions.Add([PSCustomObject]@{
- subscriptionId = $subscriptionExludedOtherTenantDetail.subscriptionId
- subscriptionName = $subscriptionExludedOtherTenantDetail.displayName
- outOfScopeReason = "Foreign tenant: Id: $($subscriptionExludedOtherTenantDetail.tenantId)"
- ManagementGroupId = ''
- ManagementGroupName = ''
- Level = ''
- })
- }
- }
-
- if ($htsubscriptionsFromEntitiesThatAreNotInGetSubscriptions.keys.count -gt 0) {
- foreach ($subscriptionExludedInEntitiesNotInSubscriptions in $htsubscriptionsFromEntitiesThatAreNotInGetSubscriptions.keys) {
- $subscriptionExludedInEntitiesNotInSubscriptionsDetail = $htsubscriptionsFromEntitiesThatAreNotInGetSubscriptions.($subscriptionExludedInEntitiesNotInSubscriptions)
- $null = $script:outOfScopeSubscriptions.Add([PSCustomObject]@{
- subscriptionId = $subscriptionExludedInEntitiesNotInSubscriptions
- subscriptionName = $subscriptionExludedInEntitiesNotInSubscriptionsDetail.properties.displayName
- outOfScopeReason = 'Sub in GetEntities, not in GetSubscriptions'
- ManagementGroupId = ''
- ManagementGroupName = ''
- Level = ''
- })
- }
- }
-
- foreach ($childrenSubscription in $childrenSubscriptions) {
-
- $sub = $htAllSubscriptionsFromAPI.($childrenSubscription.name)
- if ($sub.subDetails.subscriptionPolicies.quotaId.startswith('AAD_', 'CurrentCultureIgnoreCase') -or $sub.subDetails.state -ne 'Enabled') {
- if (($sub.subDetails.subscriptionPolicies.quotaId).startswith('AAD_', 'CurrentCultureIgnoreCase')) {
- $null = $script:outOfScopeSubscriptions.Add([PSCustomObject]@{
- subscriptionId = $childrenSubscription.name
- subscriptionName = $childrenSubscription.properties.displayName
- outOfScopeReason = "QuotaId: AAD_ (State: $($sub.subDetails.state))"
- ManagementGroupId = $htSubscriptionsMgPath.($childrenSubscription.name).Parent
- ManagementGroupName = $htSubscriptionsMgPath.($childrenSubscription.name).ParentName
- Level = $htSubscriptionsMgPath.($childrenSubscription.name).level
- })
- }
- if ($sub.subDetails.state -ne 'Enabled') {
- $null = $script:outOfScopeSubscriptions.Add([PSCustomObject]@{
- subscriptionId = $childrenSubscription.name
- subscriptionName = $childrenSubscription.properties.displayName
- outOfScopeReason = "State: $($sub.subDetails.state)"
- ManagementGroupId = $htSubscriptionsMgPath.($childrenSubscription.name).Parent
- ManagementGroupName = $htSubscriptionsMgPath.($childrenSubscription.name).ParentName
- Level = $htSubscriptionsMgPath.($childrenSubscription.name).level
- })
- }
- }
- else {
- if ($SubscriptionQuotaIdWhitelist[0] -ne 'undefined') {
- $whitelistMatched = 'unknown'
- foreach ($subscriptionQuotaIdWhitelistQuotaId in $SubscriptionQuotaIdWhitelist) {
- if (($sub.subDetails.subscriptionPolicies.quotaId).startswith($subscriptionQuotaIdWhitelistQuotaId, 'CurrentCultureIgnoreCase')) {
- $whitelistMatched = 'inWhitelist'
- }
- }
-
- if ($whitelistMatched -eq 'inWhitelist') {
- #write-host "$($childrenSubscription.properties.displayName) in whitelist"
- $null = $script:subsToProcessInCustomDataCollection.Add([PSCustomObject]@{
- subscriptionId = $childrenSubscription.name
- subscriptionName = $childrenSubscription.properties.displayName
- subscriptionQuotaId = $sub.subDetails.subscriptionPolicies.quotaId
- })
- }
- else {
- #Write-Host " preCustomDataCollection: $($childrenSubscription.properties.displayName) ($($childrenSubscription.name)) Subscription Quota Id: $($sub.subDetails.subscriptionPolicies.quotaId) is out of scope for Azure Governance Visualizer (not in Whitelist)"
- $null = $script:outOfScopeSubscriptions.Add([PSCustomObject]@{
- subscriptionId = $childrenSubscription.name
- subscriptionName = $childrenSubscription.properties.displayName
- outOfScopeReason = "QuotaId: '$($sub.subDetails.subscriptionPolicies.quotaId)' not in Whitelist"
- ManagementGroupId = $htSubscriptionsMgPath.($childrenSubscription.name).Parent
- ManagementGroupName = $htSubscriptionsMgPath.($childrenSubscription.name).ParentName
- Level = $htSubscriptionsMgPath.($childrenSubscription.name).level
- })
- }
- }
- else {
- $null = $script:subsToProcessInCustomDataCollection.Add([PSCustomObject]@{
- subscriptionId = $childrenSubscription.name
- subscriptionName = $childrenSubscription.properties.displayName
- subscriptionQuotaId = $sub.subDetails.subscriptionPolicies.quotaId
- })
- }
- }
- }
-
- if ($subsToProcessInCustomDataCollection.Count -lt $childrenSubscriptionsCount) {
- Write-Host " $($subsToProcessInCustomDataCollection.Count) of $($childrenSubscriptionsCount) Subscriptions picked for processing" -ForegroundColor yellow
- }
- else {
- Write-Host " $($subsToProcessInCustomDataCollection.Count) of $($childrenSubscriptionsCount) Subscriptions picked for processing"
- }
-
-
- if ($outOfScopeSubscriptions.Count -gt 0) {
- Write-Host " $($outOfScopeSubscriptions.Count) Subscriptions excluded" -ForegroundColor yellow
- $outOfScopeSubscriptionsGroupedByOutOfScopeReason = $outOfScopeSubscriptions | Group-Object -Property outOfScopeReason
- foreach ($exclusionreason in $outOfScopeSubscriptionsGroupedByOutOfScopeReason) {
- Write-Host " $($exclusionreason.Count): $($exclusionreason.Name) ($($exclusionreason.Group.subscriptionId -join ', '))"
- }
-
- foreach ($outOfScopeSubscription in $outOfScopeSubscriptions) {
- $script:htOutOfScopeSubscriptions.($outOfScopeSubscription.subscriptionId) = @{
- subscriptionId = $outOfScopeSubscription.subscriptionId
- subscriptionName = $outOfScopeSubscription.subscriptionName
- outOfScopeReason = $outOfScopeSubscription.outOfScopeReason
- ManagementGroupId = $outOfScopeSubscription.ManagementGroupId
- ManagementGroupName = $outOfScopeSubscription.ManagementGroupName
- Level = $outOfScopeSubscription.Level
- }
- }
- }
- else {
- Write-Host " $($outOfScopeSubscriptions.Count) Subscriptions excluded"
- }
- $script:subsToProcessInCustomDataCollectionCount = ($subsToProcessInCustomDataCollection).Count
-
- $end = Get-Date
- Write-Host "Subscription picking duration: $((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds"
-}
-function detectPolicyEffect {
- [CmdletBinding()]
- Param
- (
- [object]
- $policyDefinition
- )
-
- $htEffect = @{
- defaultValue = 'n/a'
- allowedValues = 'n/a'
- fixedValue = 'n/a'
- }
- if (-not [string]::IsNullOrWhiteSpace($policyDefinition.properties.policyRule.then.effect)) {
- if ($policyDefinition.properties.policyRule.then.effect -in $ValidPolicyEffects) {
- # $arrayeffect += "fixed: $($policyDefinition.properties.policyRule.then.effect)"
- # return $arrayeffect
- $htEffect.fixedValue = $policyDefinition.properties.policyRule.then.effect
- return $htEffect
- }
- else {
- $Regex = [Regex]::new("(?<=\[parameters\(')(.*)(?='\)\])")
- $Match = $Regex.Match($policyDefinition.properties.policyRule.then.effect)
- if ($Match.Success) {
- if (-not [string]::IsNullOrWhiteSpace($policyDefinition.properties.parameters.($Match.Value))) {
-
- #defaultValue
- if (($policyDefinition.properties.parameters.($Match.Value) | Get-Member).name -contains 'defaultvalue') {
- if (-not [string]::IsNullOrWhiteSpace($policyDefinition.properties.parameters.($Match.Value).defaultValue)) {
- if ($policyDefinition.properties.parameters.($Match.Value).defaultValue -in $ValidPolicyEffects) {
- #$arrayeffect += "default: $($policyDefinition.properties.parameters.($Match.Value).defaultValue)"
- $htEffect.defaultValue = $policyDefinition.properties.parameters.($Match.Value).defaultValue
- }
- else {
- Write-Host "invalid defaultValue effect $($policyDefinition.properties.parameters.($Match.Value).defaultValue) - $($policyDefinition.name) ($($policyDefinition.properties.policyType))"
- }
- }
- else {
- Write-Host "defaultValue empty - $($policyDefinition.name) ($($policyDefinition.properties.policyType))"
- }
- }
- else {
- Write-Host "finding: Policy has no defaultvalue for effect: $($policyDefinition.id) ($($policyDefinition.properties.policyType))"
- }
- #allowedValues
- if (($policyDefinition.properties.parameters.($Match.Value) | Get-Member).name -contains 'allowedValues') {
- if (-not [string]::IsNullOrWhiteSpace($policyDefinition.properties.parameters.($Match.Value).allowedValues)) {
- if ($policyDefinition.properties.parameters.($Match.Value).allowedValues.Count -gt 0) {
- #Write-Host "allowedValues count $($policyDefinition.properties.parameters.($Match.Value).allowedValues) - $($policyDefinition.name) ($($policyDefinition.properties.policyType))"
- $arrayAllowed = @()
- foreach ($allowedValue in $policyDefinition.properties.parameters.($Match.Value).allowedValues) {
- if ($allowedValue -in $ValidPolicyEffects) {
- $arrayAllowed += $allowedValue
- }
- else {
- Write-Host "invalid allowedValue effect $($allowedValue) - $($policyDefinition.name) ($($policyDefinition.properties.policyType))"
- }
- }
- #$arrayeffect += "allowed: $(($arrayAllowed | Sort-Object) -join ', ')"
- $htEffect.allowedValues = ($arrayAllowed | Sort-Object) -join ','
- }
- }
- else {
- Write-Host "allowedValues empty - $($policyDefinition.name) ($($policyDefinition.properties.policyType))"
- }
- }
- else {
- Write-Host "no allowedValues- $($policyDefinition.name) ($($policyDefinition.properties.policyType))"
- }
-
- }
- else {
- Write-Host "unexpected - $($policyDefinition.name) ($($policyDefinition.properties.policyType))"
- }
-
- return $htEffect
- }
- }
- }
- else {
- Write-Host "no then effect - $($policyDefinition.name) ($($policyDefinition.properties.policyType))"
- }
- return $htEffect
-}
-function exportBaseCSV {
- if (-not $NoCsvExport) {
- Write-Host "Exporting CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName).csv'"
- $startBuildCSV = Get-Date
-
- $outprops = $newtable[0].PSObject.Properties.Name
- if (-not $HierarchyMapOnly -and -not $HierarchyMapOnlyCustomDataJSON) {
- $outprops.Set($outprops.IndexOf('PolicyAssignmentNotScopes'), @{L = 'PolicyAssignmentNotScopes'; E = { ($_.PolicyAssignmentNotScopes -join "$CsvDelimiterOpposite ") } })
- }
- if ($CsvExportUseQuotesAsNeeded) {
- $newTable | Sort-Object -Property level, mgId, SubscriptionId, PolicyAssignmentId, RoleAssignmentId, BlueprintId, BlueprintAssignmentId | Select-Object -Property $outprops -ExcludeProperty PolicyAssignmentParameters | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).csv" -Delimiter "$csvDelimiter" -NoTypeInformation -UseQuotes AsNeeded
- }
- else {
- $newTable | Sort-Object -Property level, mgId, SubscriptionId, PolicyAssignmentId, RoleAssignmentId, BlueprintId, BlueprintAssignmentId | Select-Object -Property $outprops -ExcludeProperty PolicyAssignmentParameters | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
-
- $endBuildCSV = Get-Date
- Write-Host "Exporting CSV total duration: $((New-TimeSpan -Start $startBuildCSV -End $endBuildCSV).TotalMinutes) minutes ($((New-TimeSpan -Start $startBuildCSV -End $endBuildCSV).TotalSeconds) seconds)"
- }
-}
-function exportResourceLocks {
- $arrayResourceLocks4CSV = [System.Collections.ArrayList]@()
- foreach ($sub in $htResourceLocks.Keys) {
- $hlper = $htSubscriptionsMgPath.($sub)
- $subscriptionDisplayName = $hlper.DisplayName
- $mgPath = $hlper.ParentNameChainDelimited
- #sub
- if ($htResourceLocks.($sub).SubscriptionLocksCannotDeleteCount -eq 1) {
- $null = $arrayResourceLocks4CSV.Add([PSCustomObject]@{
- SubscriptionId = $sub
- SubscriptionName = $subscriptionDisplayName
- MGPath = $mgPath
- ScopeType = 'Subscription'
- Lock = 'CannotDelete'
- Id = "/subscriptions/$sub"
- ResourceType = 'Microsoft.Resources/subscriptions'
- })
- }
- if ($htResourceLocks.($sub).SubscriptionLocksReadOnlyCount -eq 1) {
- $null = $arrayResourceLocks4CSV.Add([PSCustomObject]@{
- SubscriptionId = $sub
- SubscriptionName = $subscriptionDisplayName
- MGPath = $mgPath
- ScopeType = 'Subscription'
- Lock = 'ReadOnly'
- Id = "/subscriptions/$sub"
- ResourceType = 'Microsoft.Resources/subscriptions'
- })
- }
- #rg
- if ($htResourceLocks.($sub).ResourceGroupsLocksCannotDeleteCount -gt 0) {
- foreach ($res in $htResourceLocks.($sub).ResourceGroupsLocksCannotDelete) {
- $null = $arrayResourceLocks4CSV.Add([PSCustomObject]@{
- SubscriptionId = $sub
- SubscriptionName = $subscriptionDisplayName
- MGPath = $mgPath
- ScopeType = 'ResourceGroup'
- Lock = 'CannotDelete'
- Id = $res.rg
- ResourceType = 'Microsoft.Resources/subscriptions/resourceGroups'
- })
- }
- }
- if ($htResourceLocks.($sub).ResourceGroupsLocksReadOnlyCount -gt 0) {
- foreach ($res in $htResourceLocks.($sub).ResourceGroupsLocksReadOnly) {
- $null = $arrayResourceLocks4CSV.Add([PSCustomObject]@{
- SubscriptionId = $sub
- SubscriptionName = $subscriptionDisplayName
- MGPath = $mgPath
- ScopeType = 'ResourceGroup'
- Lock = 'ReadOnly'
- Id = $res.rg
- ResourceType = 'Microsoft.Resources/subscriptions/resourceGroups'
- })
- }
- }
- #res
- if ($htResourceLocks.($sub).ResourcesLocksCannotDeleteCount -gt 0) {
- foreach ($res in $htResourceLocks.($sub).ResourcesLocksCannotDelete) {
- $resSplit = ($res.res -split '/')
- $null = $arrayResourceLocks4CSV.Add([PSCustomObject]@{
- SubscriptionId = $sub
- SubscriptionName = $subscriptionDisplayName
- MGPath = $mgPath
- ScopeType = 'Resource'
- Lock = 'CannotDelete'
- Id = $res.res
- ResourceType = "$($resSplit[6])/$($resSplit[7])"
- })
- }
- }
- if ($htResourceLocks.($sub).ResourcesLocksReadOnlyCount -gt 0) {
- foreach ($res in $htResourceLocks.($sub).ResourcesLocksReadOnly) {
- $resSplit = ($res.res -split '/')
- $null = $arrayResourceLocks4CSV.Add([PSCustomObject]@{
- SubscriptionId = $sub
- SubscriptionName = $subscriptionDisplayName
- MGPath = $mgPath
- ScopeType = 'Resource'
- Lock = 'ReadOnly'
- Id = $res.res
- ResourceType = "$($resSplit[6])/$($resSplit[7])"
- })
- }
- }
- }
- if ($arrayResourceLocks4CSV.count -gt 0) {
- if (-not $NoCsvExport) {
- Write-Host "Exporting ResourceLocks CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName)_ResourceLocks.csv'"
- $arrayResourceLocks4CSV | Sort-Object -Property ScopeType, Lock, SubscriptionId, Id | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_ResourceLocks.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
- }
-}
-function getConsumption {
-
- function addToAllConsumptionData {
- [CmdletBinding()]Param(
- [Parameter(Mandatory)]
- [object]
- $consumptiondataFromAPI
- )
-
- foreach ($consumptionline in $consumptiondataFromAPI.properties.rows) {
- $hlper = $htSubscriptionsMgPath.($consumptionline[1])
-
- $null = $script:allConsumptionData.Add([PSCustomObject]@{
- "$($consumptiondataFromAPI.properties.columns.name[0])" = [decimal]$consumptionline[0]
- "$($consumptiondataFromAPI.properties.columns.name[1])" = $consumptionline[1]
- SubscriptionName = $hlper.DisplayName
- SubscriptionMgPath = $hlper.ParentNameChainDelimited
- "$($consumptiondataFromAPI.properties.columns.name[2])" = $consumptionline[2]
- "$($consumptiondataFromAPI.properties.columns.name[3])" = $consumptionline[3]
- "$($consumptiondataFromAPI.properties.columns.name[4])" = $consumptionline[4]
- "$($consumptiondataFromAPI.properties.columns.name[5])" = $consumptionline[5]
- "$($consumptiondataFromAPI.properties.columns.name[6])" = $consumptionline[6]
- })
- }
- }
-
- $startConsumptionData = Get-Date
-
- #cost only for whitelisted quotaId
- if ($SubscriptionQuotaIdWhitelist[0] -ne 'undefined') {
- if ($subsToProcessInCustomDataCollectionCount -gt 0) {
- #region mgScopeWhitelisted
- #$subscriptionIdsOptimizedForBody = '"{0}"' -f ($subsToProcessInCustomDataCollection.subscriptionId -join '","')
- $currenttask = "Getting Consumption data (scope MG '$($ManagementGroupId)') for $($subsToProcessInCustomDataCollectionCount) Subscriptions (QuotaId Whitelist: '$($SubscriptionQuotaIdWhitelist -join ', ')') for period $AzureConsumptionPeriod days ($azureConsumptionStartDate - $azureConsumptionEndDate)"
- Write-Host "$currentTask"
- #https://docs.microsoft.com/en-us/rest/api/cost-management/query/usage
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)/providers/Microsoft.CostManagement/query?api-version=2019-11-01&`$top=5000"
- $method = 'POST'
-
- $counterBatch = [PSCustomObject] @{ Value = 0 }
- $batchSize = 100
- $subscriptionsBatch = ($subsToProcessInCustomDataCollection | Sort-Object -Property subscriptionQuotaId) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
- $batchCnt = 0
-
- foreach ($batch in $subscriptionsBatch) {
- $batchCnt++
- $subscriptionIdsOptimizedForBody = '"{0}"' -f (($batch.Group).subscriptionId -join '","')
- $currenttask = "Getting Consumption data #batch$($batchCnt)/$(($subscriptionsBatch | Measure-Object).Count) (scope MG '$($ManagementGroupId)') for $(($batch.Group).Count) Subscriptions (QuotaId Whitelist: '$($SubscriptionQuotaIdWhitelist -join ', ')') for period $AzureConsumptionPeriod days ($azureConsumptionStartDate - $azureConsumptionEndDate)"
- Write-Host "$currentTask" -ForegroundColor Cyan
-
- $body = @"
-{
-"type": "ActualCost",
-"dataset": {
- "granularity": "none",
- "filter": {
- "dimensions": {
- "name": "SubscriptionId",
- "operator": "In",
- "values": [
- $($subscriptionIdsOptimizedForBody)
- ]
- }
- },
- "aggregation": {
- "totalCost": {
- "name": "PreTaxCost",
- "function": "Sum"
- }
- },
- "grouping": [
- {
- "type": "Dimension",
- "name": "SubscriptionId"
- },
- {
- "type": "Dimension",
- "name": "ResourceId"
- },
- {
- "type": "Dimension",
- "name": "ResourceType"
- },
- {
- "type": "Dimension",
- "name": "MeterCategory"
- },
- {
- "type": "Dimension",
- "name": "ChargeType"
- }
- ]
-},
-"timeframe": "Custom",
-"timeperiod": {
- "from": "$($azureConsumptionStartDate)",
- "to": "$($azureConsumptionEndDate)"
-}
-}
-"@
-
- $mgConsumptionData = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -currentTask $currentTask -listenOn 'ContentProperties'
- #endregion mgScopeWhitelisted
-
- <#test
- #$mgConsumptionData = "OfferNotSupported"
- if ($batchCnt -eq 1){
- $mgConsumptionData = "OfferNotSupported"
- }
- #>
-
- if ($mgConsumptionData -eq 'Unauthorized' -or $mgConsumptionData -eq 'OfferNotSupported' -or $mgConsumptionData -eq 'NoValidSubscriptions') {
- if (-not $script:htConsumptionExceptionLog.Mg.($ManagementGroupId)) {
- $script:htConsumptionExceptionLog.Mg.($ManagementGroupId) = @{}
- }
- $script:htConsumptionExceptionLog.Mg.($ManagementGroupId).($batchCnt) = @{}
- $script:htConsumptionExceptionLog.Mg.($ManagementGroupId).($batchCnt).Exception = $mgConsumptionData
- $script:htConsumptionExceptionLog.Mg.($ManagementGroupId).($batchCnt).Subscriptions = ($batch.Group).subscriptionId
- Write-Host " Switching to 'foreach Subscription' Subscription scope mode. Getting Consumption data #batch$($batchCnt) using Management Group scope failed."
- #region subScopewhitelisted
- $body = @"
-{
-"type": "ActualCost",
-"dataset": {
- "granularity": "none",
- "aggregation": {
- "totalCost": {
- "name": "PreTaxCost",
- "function": "Sum"
- }
- },
- "grouping": [
- {
- "type": "Dimension",
- "name": "SubscriptionId"
- },
- {
- "type": "Dimension",
- "name": "ResourceId"
- },
- {
- "type": "Dimension",
- "name": "ResourceType"
- },
- {
- "type": "Dimension",
- "name": "MeterCategory"
- },
- {
- "type": "Dimension",
- "name": "ChargeType"
- }
- ]
-},
-"timeframe": "Custom",
-"timeperiod": {
- "from": "$($azureConsumptionStartDate)",
- "to": "$($azureConsumptionEndDate)"
-}
-}
-"@
- $funcAddToAllConsumptionData = $function:addToAllConsumptionData.ToString()
- $batch.Group | ForEach-Object -Parallel {
- $subIdToProcess = $_.subscriptionId
- $subNameToProcess = $_.subscriptionName
- $subscriptionQuotaIdToProcess = $_.subscriptionQuotaId
- #region UsingVARs
- $body = $using:body
- $azureConsumptionStartDate = $using:azureConsumptionStartDate
- $azureConsumptionEndDate = $using:azureConsumptionEndDate
- $SubscriptionQuotaIdWhitelist = $using:SubscriptionQuotaIdWhitelist
- #fromOtherFunctions
- $azAPICallConf = $using:azAPICallConf
- $scriptPath = $using:ScriptPath
- #Array&HTs
- $allConsumptionData = $using:allConsumptionData
- $htSubscriptionsMgPath = $using:htSubscriptionsMgPath
- $htAllSubscriptionsFromAPI = $using:htAllSubscriptionsFromAPI
- $htConsumptionExceptionLog = $using:htConsumptionExceptionLog
- #other
- $function:addToAllConsumptionData = $using:funcAddToAllConsumptionData
- #endregion UsingVARs
-
- $currentTask = " Getting Consumption data (scope Sub $($subNameToProcess) '$($subIdToProcess)' ($($subscriptionQuotaIdToProcess)) (whitelist))"
- #test
- Write-Host $currentTask
- #https://docs.microsoft.com/en-us/rest/api/cost-management/query/usage
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($subIdToProcess)/providers/Microsoft.CostManagement/query?api-version=2019-11-01&`$top=5000"
- $method = 'POST'
- $subConsumptionData = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -currentTask $currentTask -listenOn 'ContentProperties'
- if ($subConsumptionData -eq 'Unauthorized' -or $subConsumptionData -eq 'OfferNotSupported' -or $subConsumptionData -eq 'InvalidQueryDefinition' -or $subConsumptionData -eq 'NonValidWebDirectAIRSOfferType' -or $subConsumptionData -eq 'NotFoundNotSupported' -or $subConsumptionData -eq 'IndirectCostDisabled') {
- Write-Host " Failed ($subConsumptionData) - Getting Consumption data (scope Sub $($subNameToProcess) '$($subIdToProcess)' ($($subscriptionQuotaIdToProcess)) (whitelist))"
- $hlper = $htAllSubscriptionsFromAPI.($subIdToProcess).subDetails
- $hlper2 = $htSubscriptionsMgPath.($subIdToProcess)
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess) = @{}
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).Exception = $subConsumptionData
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).SubscriptionId = $subIdToProcess
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).SubscriptionName = $hlper.displayName
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).QuotaId = $hlper.subscriptionPolicies.quotaId
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).mgPath = $hlper2.ParentNameChainDelimited
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).mgParent = $hlper2.Parent
- Continue
- }
- else {
- Write-Host " $($subConsumptionData.Count) Consumption data entries ((scope Sub $($subNameToProcess) '$($subIdToProcess)' ($($subscriptionQuotaIdToProcess))))"
- if ($subConsumptionData.Count -gt 0) {
- addToAllConsumptionData -consumptiondataFromAPI $subConsumptionData
- <#
- foreach ($consumptionEntry in $subConsumptionData) {
- if ($consumptionEntry.PreTaxCost -ne 0) {
- $null = $script:allConsumptionData.Add($consumptionEntry)
- }
- }
- #>
-
- }
- }
- } -ThrottleLimit $ThrottleLimit
- #endregion subScopewhitelisted
- }
- else {
- Write-Host " $($mgConsumptionData.Count) Consumption data entries"
- if ($mgConsumptionData.Count -gt 0) {
- addToAllConsumptionData -consumptiondataFromAPI $mgConsumptionData
- <#
- foreach ($consumptionEntry in $mgConsumptionData) {
- if ($consumptionEntry.PreTaxCost -ne 0) {
- $null = $script:allConsumptionData.Add($consumptionEntry)
- }
- }
- #>
- }
- }
- }
- }
- else {
- $detailShowStopperResult = 'NoWhitelistSubscriptionsPresent'
- Write-Host ' No Subscriptions matching whitelist present, skipping Consumption data processing'
- #überprüfen
- }
- }
- else {
-
- if ($subsToProcessInCustomDataCollectionCount -gt 0) {
- #region mgScope
- $currenttask = "Getting Consumption data (scope MG '$($ManagementGroupId)') for period $AzureConsumptionPeriod days ($azureConsumptionStartDate - $azureConsumptionEndDate)"
- Write-Host "$currentTask"
- #https://docs.microsoft.com/en-us/rest/api/cost-management/query/usage
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)/providers/Microsoft.CostManagement/query?api-version=2019-11-01&`$top=5000"
- $method = 'POST'
- $body = @"
-{
- "type": "ActualCost",
- "dataset": {
- "granularity": "none",
- "aggregation": {
- "totalCost": {
- "name": "PreTaxCost",
- "function": "Sum"
- }
- },
- "grouping": [
- {
- "type": "Dimension",
- "name": "SubscriptionId"
- },
- {
- "type": "Dimension",
- "name": "ResourceId"
- },
- {
- "type": "Dimension",
- "name": "ResourceType"
- },
- {
- "type": "Dimension",
- "name": "MeterCategory"
- },
- {
- "type": "Dimension",
- "name": "ChargeType"
- }
- ]
- },
- "timeframe": "Custom",
- "timeperiod": {
- "from": "$($azureConsumptionStartDate)",
- "to": "$($azureConsumptionEndDate)"
- }
-}
-"@
- #$script:allConsumptionData = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -currentTask $currentTask -listenOn 'ContentProperties'
- $allConsumptionDataAPIResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -currentTask $currentTask -listenOn 'ContentProperties'
- #endregion mgScope
-
- #test
- #$allConsumptionData = "OfferNotSupported"
-
- if ($allConsumptionDataAPIResult -eq 'AccountCostDisabled' <#-or $allConsumptionDataAPIResult -eq 'NoValidSubscriptions'#>) {
- if ($allConsumptionDataAPIResult -eq 'AccountCostDisabled') {
- $detailShowStopperResult = $allConsumptionDataAPIResult
- }
- <#if ($allConsumptionDataAPIResult -eq 'NoValidSubscriptions') {
- $detailShowStopperResult = $allConsumptionDataAPIResult
- }#>
- }
- else {
- if ($allConsumptionDataAPIResult -eq 'Unauthorized' -or $allConsumptionDataAPIResult -eq 'OfferNotSupported' -or $allConsumptionDataAPIResult -eq 'NoValidSubscriptions' -or $allConsumptionDataAPIResult -eq 'tooManySubscriptions') {
- $script:htConsumptionExceptionLog.Mg.($ManagementGroupId) = @{}
- $script:htConsumptionExceptionLog.Mg.($ManagementGroupId).Exception = $allConsumptionDataAPIResult
- Write-Host " Switching to 'foreach Subscription' mode. Getting Consumption data using Management Group scope failed."
- #region subScope
- $body = @"
-{
- "type": "ActualCost",
- "dataset": {
- "granularity": "none",
- "aggregation": {
- "totalCost": {
- "name": "PreTaxCost",
- "function": "Sum"
- }
- },
- "grouping": [
- {
- "type": "Dimension",
- "name": "SubscriptionId"
- },
- {
- "type": "Dimension",
- "name": "ResourceId"
- },
- {
- "type": "Dimension",
- "name": "ResourceType"
- },
- {
- "type": "Dimension",
- "name": "MeterCategory"
- },
- {
- "type": "Dimension",
- "name": "ChargeType"
- }
- ]
- },
- "timeframe": "Custom",
- "timeperiod": {
- "from": "$($azureConsumptionStartDate)",
- "to": "$($azureConsumptionEndDate)"
- }
-}
-"@
-
- $funcAddToAllConsumptionData = $function:addToAllConsumptionData.ToString()
- $subsToProcessInCustomDataCollection | ForEach-Object -Parallel {
- $subIdToProcess = $_.subscriptionId
- $subNameToProcess = $_.subscriptionName
- $subscriptionQuotaIdToProcess = $_.subscriptionQuotaId
- #region UsingVARs
- $body = $using:body
- $azureConsumptionStartDate = $using:azureConsumptionStartDate
- $azureConsumptionEndDate = $using:azureConsumptionEndDate
- #fromOtherFunctions
- $azAPICallConf = $using:azAPICallConf
- $scriptPath = $using:ScriptPath
- #Array&HTs
- $htSubscriptionsMgPath = $using:htSubscriptionsMgPath
- $htAllSubscriptionsFromAPI = $using:htAllSubscriptionsFromAPI
- $allConsumptionData = $using:allConsumptionData
- $htConsumptionExceptionLog = $using:htConsumptionExceptionLog
- #other
- $function:addToAllConsumptionData = $using:funcAddToAllConsumptionData
- #endregion UsingVARs
-
- $currentTask = " Getting Consumption data (scope Sub $($subNameToProcess) '$($subIdToProcess)' ($($subscriptionQuotaIdToProcess)))"
- #test
- Write-Host $currentTask
- #https://docs.microsoft.com/en-us/rest/api/cost-management/query/usage
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($subIdToProcess)/providers/Microsoft.CostManagement/query?api-version=2019-11-01&`$top=5000"
- $method = 'POST'
- $subConsumptionData = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -currentTask $currentTask -listenOn 'ContentProperties'
- if ($subConsumptionData -eq 'Unauthorized' -or $subConsumptionData -eq 'OfferNotSupported' -or $subConsumptionData -eq 'InvalidQueryDefinition' -or $subConsumptionData -eq 'NonValidWebDirectAIRSOfferType' -or $subConsumptionData -eq 'NotFoundNotSupported' -or $subConsumptionData -eq 'IndirectCostDisabled') {
- Write-Host " Failed ($subConsumptionData) - Getting Consumption data (scope Sub $($subNameToProcess) '$($subIdToProcess)' ($($subscriptionQuotaIdToProcess)))"
- $hlper = $htAllSubscriptionsFromAPI.($subIdToProcess).subDetails
- $hlper2 = $htSubscriptionsMgPath.($subIdToProcess)
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess) = @{}
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).Exception = $subConsumptionData
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).SubscriptionId = $subIdToProcess
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).SubscriptionName = $hlper.displayName
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).QuotaId = $hlper.subscriptionPolicies.quotaId
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).mgPath = $hlper2.ParentNameChainDelimited
- $script:htConsumptionExceptionLog.Sub.($subIdToProcess).mgParent = $hlper2.Parent
- Continue
- }
- else {
- Write-Host " $($subConsumptionData.Count) Consumption data entries (scope Sub $($subNameToProcess) '$($subIdToProcess)' ($($subscriptionQuotaIdToProcess)))"
- if ($subConsumptionData.Count -gt 0) {
- addToAllConsumptionData -consumptiondataFromAPI $subConsumptionData
- <#
- foreach ($consumptionEntry in $subConsumptionData) {
- if ($consumptionEntry.PreTaxCost -ne 0) {
- $null = $script:allConsumptionData.Add($consumptionEntry)
- }
- }
- #>
- }
- }
- } -ThrottleLimit $ThrottleLimit
- #endregion subScope
- }
- else {
- Write-Host " $($allConsumptionDataAPIResult.properties.rows.Count) Consumption data entries"
- if ($allConsumptionDataAPIResult.properties.rows.Count -gt 0) {
- addToAllConsumptionData -consumptiondataFromAPI $allConsumptionDataAPIResult
- }
- }
- }
- }
- else {
- $detailShowStopperResult = 'NoSubscriptionsPresent'
- Write-Host ' No Subscriptions present, skipping Consumption data processing'
- }
- }
-
- if ($detailShowStopperResult -eq 'AccountCostDisabled' -or $detailShowStopperResult -eq 'NoValidSubscriptions' -or $detailShowStopperResult -eq 'NoWhitelistSubscriptionsPresent' -or $detailShowStopperResult -eq 'NoSubscriptionsPresent') {
- if ($detailShowStopperResult -eq 'AccountCostDisabled') {
- Write-Host ' Seems Access to cost data has been disabled for this Account - skipping CostManagement'
- }
- if ($detailShowStopperResult -eq 'NoValidSubscriptions') {
- Write-Host ' Seems there are no valid Subscriptions present - skipping CostManagement'
- }
- if ($detailShowStopperResult -eq 'NoWhitelistSubscriptionsPresent') {
- Write-Host " Seems there are no Subscriptions present that match the whitelist ($($SubscriptionQuotaIdWhitelist -join ', ')) - skipping CostManagement"
- }
- if ($detailShowStopperResult -eq 'NoSubscriptionsPresent') {
- Write-Host ' Seems there are no Subscriptions present - skipping CostManagement'
- }
- Write-Host " Action: Setting switch parameter 'DoAzureConsumption' to false"
- $azAPICallConf['htParameters'].DoAzureConsumption = $false
- }
- else {
- Write-Host ' Checking returned Consumption data'
- $script:allConsumptionDataCount = $allConsumptionData.Count
-
- if ($allConsumptionDataCount -gt 0) {
-
- $script:allConsumptionData = $allConsumptionData.where( { $_.PreTaxCost -ne 0 } )
- $script:allConsumptionDataCount = $allConsumptionData.Count
-
- if ($allConsumptionDataCount -gt 0) {
- Write-Host " $($allConsumptionDataCount) relevant Consumption data entries"
-
- $script:consumptionData = $allConsumptionData
- $script:consumptionDataGroupedByCurrency = $consumptionData | Group-Object -Property Currency
-
- foreach ($currency in $consumptionDataGroupedByCurrency) {
-
- #subscriptions
- $groupAllConsumptionDataPerCurrencyBySubscriptionId = $currency.group | Group-Object -Property SubscriptionId
- foreach ($subscriptionId in $groupAllConsumptionDataPerCurrencyBySubscriptionId) {
-
- $subTotalCost = ($subscriptionId.Group.PreTaxCost | Measure-Object -Sum).Sum
- $script:htAzureConsumptionSubscriptions.($subscriptionId.Name) = @{}
- $script:htAzureConsumptionSubscriptions.($subscriptionId.Name).ConsumptionData = $subscriptionId.group
- $script:htAzureConsumptionSubscriptions.($subscriptionId.Name).TotalCost = $subTotalCost
- $script:htAzureConsumptionSubscriptions.($subscriptionId.Name).Currency = $currency.Name
- $resourceTypes = $subscriptionId.Group.ResourceType | Sort-Object -Unique
-
- foreach ($parentMg in $htSubscriptionsMgPath.($subscriptionId.Name).ParentNameChain) {
-
- if (-not $htManagementGroupsCost.($parentMg)) {
- $script:htManagementGroupsCost.($parentMg) = @{}
- $script:htManagementGroupsCost.($parentMg).currencies = $currency.Name
- $script:htManagementGroupsCost.($parentMg)."mgTotalCost_$($currency.Name)" = $subTotalCost #[decimal]$subTotalCost
- $script:htManagementGroupsCost.($parentMg)."resourcesThatGeneratedCost_$($currency.Name)" = ($subscriptionId.Group.ResourceId | Sort-Object -Unique | Measure-Object).Count
- $script:htManagementGroupsCost.($parentMg).resourcesThatGeneratedCostCurrencyIndependent = ($subscriptionId.Group.ResourceId | Sort-Object -Unique | Measure-Object).Count
- $script:htManagementGroupsCost.($parentMg)."subscriptionsThatGeneratedCost_$($currency.Name)" = 1
- $script:htManagementGroupsCost.($parentMg).subscriptionsThatGeneratedCostCurrencyIndependent = 1
- $script:htManagementGroupsCost.($parentMg)."resourceTypesThatGeneratedCost_$($currency.Name)" = $resourceTypes
- $script:htManagementGroupsCost.($parentMg).resourceTypesThatGeneratedCostCurrencyIndependent = $resourceTypes
- $script:htManagementGroupsCost.($parentMg)."consumptionDataSubscriptions_$($currency.Name)" = $subscriptionId.group
- $script:htManagementGroupsCost.($parentMg).consumptionDataSubscriptions = $subscriptionId.group
- }
- else {
- $newMgTotalCost = $htManagementGroupsCost.($parentMg)."mgTotalCost_$($currency.Name)" + $subTotalCost #[decimal]$subTotalCost
- $script:htManagementGroupsCost.($parentMg)."mgTotalCost_$($currency.Name)" = $newMgTotalCost #[decimal]$newMgTotalCost
-
- $currencies = [array]$htManagementGroupsCost.($parentMg).currencies
- if ($currencies -notcontains $currency.Name) {
- $currencies += $currency.Name
- $script:htManagementGroupsCost.($parentMg).currencies = $currencies
- }
-
- #currency based
- $resourcesThatGeneratedCost = $htManagementGroupsCost.($parentMg)."resourcesThatGeneratedCost_$($currency.Name)" + ($subscriptionId.Group.ResourceId | Sort-Object -Unique | Measure-Object).Count
- $script:htManagementGroupsCost.($parentMg)."resourcesThatGeneratedCost_$($currency.Name)" = $resourcesThatGeneratedCost
-
- $subscriptionsThatGeneratedCost = $htManagementGroupsCost.($parentMg)."subscriptionsThatGeneratedCost_$($currency.Name)" + 1
- $script:htManagementGroupsCost.($parentMg)."subscriptionsThatGeneratedCost_$($currency.Name)" = $subscriptionsThatGeneratedCost
-
- $consumptionDataSubscriptions = $htManagementGroupsCost.($parentMg)."consumptionDataSubscriptions_$($currency.Name)" += $subscriptionId.group
- $script:htManagementGroupsCost.($parentMg)."consumptionDataSubscriptions_$($currency.Name)" = $consumptionDataSubscriptions
-
- $resourceTypesThatGeneratedCost = $htManagementGroupsCost.($parentMg)."resourceTypesThatGeneratedCost_$($currency.Name)"
- foreach ($resourceType in $resourceTypes) {
- if ($resourceTypesThatGeneratedCost -notcontains $resourceType) {
- $resourceTypesThatGeneratedCost += $resourceType
- }
- }
- $script:htManagementGroupsCost.($parentMg)."resourceTypesThatGeneratedCost_$($currency.Name)" = $resourceTypesThatGeneratedCost
-
- #currencyIndependent
- $resourcesThatGeneratedCostCurrencyIndependent = $htManagementGroupsCost.($parentMg).resourcesThatGeneratedCostCurrencyIndependent + ($subscriptionId.Group.ResourceId | Sort-Object -Unique | Measure-Object).Count
- $script:htManagementGroupsCost.($parentMg).resourcesThatGeneratedCostCurrencyIndependent = $resourcesThatGeneratedCostCurrencyIndependent
-
- $subscriptionsThatGeneratedCostCurrencyIndependent = $htManagementGroupsCost.($parentMg).subscriptionsThatGeneratedCostCurrencyIndependent + 1
- $script:htManagementGroupsCost.($parentMg).subscriptionsThatGeneratedCostCurrencyIndependent = $subscriptionsThatGeneratedCostCurrencyIndependent
-
- $consumptionDataSubscriptionsCurrencyIndependent = $htManagementGroupsCost.($parentMg).consumptionDataSubscriptions += $subscriptionId.group
- $script:htManagementGroupsCost.($parentMg).consumptionDataSubscriptions = $consumptionDataSubscriptionsCurrencyIndependent
-
- $resourceTypesThatGeneratedCostCurrencyIndependent = $htManagementGroupsCost.($parentMg).resourceTypesThatGeneratedCostCurrencyIndependent
- foreach ($resourceType in $resourceTypes) {
- if ($resourceTypesThatGeneratedCostCurrencyIndependent -notcontains $resourceType) {
- $resourceTypesThatGeneratedCostCurrencyIndependent += $resourceType
- }
- }
- $script:htManagementGroupsCost.($parentMg).resourceTypesThatGeneratedCostCurrencyIndependent = $resourceTypesThatGeneratedCostCurrencyIndependent
- }
- }
- }
-
- $totalCost = 0
- $script:tenantSummaryConsumptionDataGrouped = $currency.group | Group-Object -Property ResourceType, ChargeType, MeterCategory
- $subsCount = ($tenantSummaryConsumptionDataGrouped.group.subscriptionId | Sort-Object -Unique | Measure-Object).Count
- $consumedServiceCount = ($tenantSummaryConsumptionDataGrouped.group.ResourceType | Sort-Object -Unique | Measure-Object).Count
- $resourceCount = ($tenantSummaryConsumptionDataGrouped.group.ResourceId | Sort-Object -Unique | Measure-Object).Count
- foreach ($consumptionline in $tenantSummaryConsumptionDataGrouped) {
-
- $costConsumptionLine = ($consumptionline.group.PreTaxCost | Measure-Object -Sum).Sum
-
- if ([math]::Round($costConsumptionLine, 2) -eq 0) {
- $cost = $costConsumptionLine.ToString('0.0000')
- }
- else {
- $cost = [math]::Round($costConsumptionLine, 2).ToString('0.00')
- }
-
- $null = $script:arrayConsumptionData.Add([PSCustomObject]@{
- ResourceType = ($consumptionline.name).split(', ')[0]
- ConsumedServiceChargeType = ($consumptionline.name).split(', ')[1]
- ConsumedServiceCategory = ($consumptionline.name).split(', ')[2]
- ConsumedServiceInstanceCount = $consumptionline.Count
- ConsumedServiceCost = $cost #[decimal]$cost
- ConsumedServiceSubscriptions = ($consumptionline.group.SubscriptionId | Sort-Object -Unique).Count
- ConsumedServiceCurrency = $currency.Name
- })
-
- $totalCost = $totalCost + $costConsumptionLine
-
- }
- if ([math]::Round($totalCost, 2) -eq 0) {
- $totalCost = $totalCost
- }
- else {
- $totalCost = [math]::Round($totalCost, 2).ToString('0.00')
- }
- $script:arrayTotalCostSummary += "$($totalCost) $($currency.Name) generated by $($resourceCount) Resources ($($consumedServiceCount) ResourceTypes) in $($subsCount) Subscriptions"
- }
- }
- else {
- Write-Host ' No relevant consumption data entries (0)'
- }
- }
-
- #region BuildConsumptionCSV
- if (-not $NoCsvExport) {
- if (-not $NoAzureConsumptionReportExportToCSV) {
- Write-Host " Exporting Consumption CSV $($outputPath)$($DirectorySeparatorChar)$($fileName)_Consumption.csv"
- $startBuildConsumptionCSV = Get-Date
- if ($CsvExportUseQuotesAsNeeded) {
- $allConsumptionData | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_Consumption.csv" -Delimiter "$csvDelimiter" -NoTypeInformation -UseQuotes AsNeeded
- }
- else {
- $allConsumptionData | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_Consumption.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
- $endBuildConsumptionCSV = Get-Date
- Write-Host " Exporting Consumption CSV total duration: $((New-TimeSpan -Start $startBuildConsumptionCSV -End $endBuildConsumptionCSV).TotalMinutes) minutes ($((New-TimeSpan -Start $startBuildConsumptionCSV -End $endBuildConsumptionCSV).TotalSeconds) seconds)"
- }
- }
- #endregion BuildConsumptionCSV
- }
- $endConsumptionData = Get-Date
- Write-Host "Getting Consumption data duration: $((New-TimeSpan -Start $startConsumptionData -End $endConsumptionData).TotalSeconds) seconds"
-}
-function getDefaultManagementGroup {
- $currentTask = 'Get Default Management Group'
- Write-Host $currentTask
- #https://docs.microsoft.com/en-us/azure/governance/management-groups/how-to/protect-resource-hierarchy#setting---default-management-group
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($azAPICallConf['checkContext'].Tenant.Id)/settings?api-version=2020-02-01"
- $method = 'GET'
- $settingsMG = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- if (($settingsMG).count -gt 0) {
- Write-Host " default ManagementGroup Id: $($settingsMG.properties.defaultManagementGroup)"
- $script:defaultManagementGroupId = $settingsMG.properties.defaultManagementGroup
- Write-Host " requireAuthorizationForGroupCreation: $($settingsMG.properties.requireAuthorizationForGroupCreation)"
- $script:requireAuthorizationForGroupCreation = $settingsMG.properties.requireAuthorizationForGroupCreation
- }
- else {
- Write-Host " default ManagementGroup: $(($azAPICallConf['checkContext']).Tenant.Id) (Tenant Root)"
- $script:defaultManagementGroupId = ($azAPICallConf['checkContext']).Tenant.Id
- $script:requireAuthorizationForGroupCreation = $false
- }
-}
-function getEntities {
- Write-Host 'Entities'
- $startEntities = Get-Date
- $currentTask = ' Getting Entities'
- Write-Host $currentTask
- #https://management.azure.com/providers/Microsoft.Management/getEntities?api-version=2020-02-01
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/getEntities?api-version=2020-02-01"
- $method = 'POST'
- $arrayEntitiesFromAPIInitial = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
- Write-Host " $($arrayEntitiesFromAPIInitial.Count) Entities returned"
-
- $script:arrayEntitiesFromAPI = [System.Collections.ArrayList]@()
- $script:htsubscriptionsFromEntitiesThatAreNotInGetSubscriptions = @{}
- foreach ($entry in $arrayEntitiesFromAPIInitial) {
- if ($entry.Type -eq '/subscriptions') {
- if ($htSubscriptionsFromOtherTenants.($entry.name)) {
- $subdetail = $htSubscriptionsFromOtherTenants.($entry.name).subdetails
- Write-Host " Excluded Subscription '$($subDetail.displayName)' ($($entry.name)) (foreign tenantId: '$($subDetail.tenantId)')" -ForegroundColor DarkRed
- continue
- }
- if (-not $htAllSubscriptionsFromAPI.($entry.name)) {
- #not contained in subscriptions
- $script:htsubscriptionsFromEntitiesThatAreNotInGetSubscriptions.($entry.name) = $entry
- Write-Host " Excluded Subscription '$($entry.properties.displayName)' ($($entry.name)) (contained in GetEntities, not contained in GetSubscriptions)" -ForegroundColor DarkRed
- continue
- }
- #test
- # if ($entry.name -eq '') {
- # $script:htsubscriptionsFromEntitiesThatAreNotInGetSubscriptions.($entry.name) = $entry
- # Write-Host " Excluded Subscription '$($entry.properties.displayName)' ($($entry.name)) (contained in GetEntities, not contained in GetSubscriptions)" -ForegroundColor DarkRed
- # continue
- # }
- }
-
- $null = $script:arrayEntitiesFromAPI.Add($entry)
- }
-
- Write-Host " $($arrayEntitiesFromAPI.Count)/$($arrayEntitiesFromAPIInitial.Count) Entities relevant"
-
- $endEntities = Get-Date
- Write-Host " Getting Entities duration: $((New-TimeSpan -Start $startEntities -End $endEntities).TotalSeconds) seconds"
-
- $startEntitiesdata = Get-Date
- Write-Host ' Processing Entities data'
- $script:htSubscriptionsMgPath = @{}
- $script:htManagementGroupsMgPath = @{}
- $script:htEntities = @{}
- $script:htEntitiesPlain = @{}
-
- foreach ($entity in $arrayEntitiesFromAPI) {
- $script:htEntitiesPlain.($entity.Name) = @{}
- $script:htEntitiesPlain.($entity.Name) = $entity
- }
-
- foreach ($entity in $arrayEntitiesFromAPI) {
- if ($entity.Type -eq '/subscriptions') {
- $parent = $entity.properties.parent.Id -replace '.*/'
- $parentId = $entity.properties.parent.Id
- $script:htSubscriptionsMgPath.($entity.name) = @{}
- $script:htSubscriptionsMgPath.($entity.name).ParentNameChain = $entity.properties.parentNameChain
- $script:htSubscriptionsMgPath.($entity.name).ParentNameChainDelimited = $entity.properties.parentNameChain -join '/'
- $script:htSubscriptionsMgPath.($entity.name).Parent = $entity.properties.parent.Id -replace '.*/'
- $script:htSubscriptionsMgPath.($entity.name).ParentName = $htEntitiesPlain.($entity.properties.parent.Id -replace '.*/').properties.displayName
- $script:htSubscriptionsMgPath.($entity.name).DisplayName = $entity.properties.displayName
- $array = $entity.properties.parentNameChain
- $array += $entity.name
- $script:htSubscriptionsMgPath.($entity.name).path = $array
- $script:htSubscriptionsMgPath.($entity.name).pathDelimited = $array -join '/'
- $script:htSubscriptionsMgPath.($entity.name).level = (($entity.properties.parentNameChain).Count - 1)
- }
- if ($entity.Type -eq 'Microsoft.Management/managementGroups') {
- if ([string]::IsNullOrEmpty($entity.properties.parent.Id)) {
- $parent = '__TenantRoot__'
- $parentId = '__TenantRoot__'
- }
- else {
- $parent = $entity.properties.parent.Id -replace '.*/'
- $parentId = $entity.properties.parent.Id
- }
- $script:htManagementGroupsMgPath.($entity.name) = @{}
- $script:htManagementGroupsMgPath.($entity.name).ParentNameChain = $entity.properties.parentNameChain
- $script:htManagementGroupsMgPath.($entity.name).ParentNameChainDelimited = $entity.properties.parentNameChain -join '/'
- $script:htManagementGroupsMgPath.($entity.name).ParentNameChainCount = ($entity.properties.parentNameChain | Measure-Object).Count
- $script:htManagementGroupsMgPath.($entity.name).Parent = $parent
- $script:htManagementGroupsMgPath.($entity.name).ChildMgsAll = ($arrayEntitiesFromAPI.where( { $_.Type -eq 'Microsoft.Management/managementGroups' -and $_.properties.ParentNameChain -contains $entity.name } )).Name
- $script:htManagementGroupsMgPath.($entity.name).ChildMgsDirect = ($arrayEntitiesFromAPI.where( { $_.Type -eq 'Microsoft.Management/managementGroups' -and $_.properties.Parent.Id -replace '.*/' -eq $entity.name } )).Name
- $script:htManagementGroupsMgPath.($entity.name).DisplayName = $entity.properties.displayName
- $script:htManagementGroupsMgPath.($entity.name).Id = ($entity.name)
- $array = $entity.properties.parentNameChain
- $array += $entity.name
- $script:htManagementGroupsMgPath.($entity.name).path = $array
- $script:htManagementGroupsMgPath.($entity.name).pathDelimited = $array -join '/'
- $script:htManagementGroupsMgPath.($entity.name).level = $array.Count
- }
-
- $script:htEntities.($entity.name) = @{}
- $script:htEntities.($entity.name).ParentNameChain = $entity.properties.parentNameChain
- $script:htEntities.($entity.name).Parent = $parent
- $script:htEntities.($entity.name).ParentId = $parentId
- if ($parent -eq '__TenantRoot__') {
- $parentDisplayName = '__TenantRoot__'
- }
- else {
- $parentDisplayName = $htEntitiesPlain.($htEntities.($entity.name).Parent).properties.displayName
- }
- $script:htEntities.($entity.name).ParentDisplayName = $parentDisplayName
- $script:htEntities.($entity.name).DisplayName = $entity.properties.displayName
- $script:htEntities.($entity.name).Id = $entity.Name
- $script:htEntities.($entity.name).Type = $entity.Type
- }
-
- Write-Host " $(($htManagementGroupsMgPath.Keys).Count) relevant Management Groups"
- Write-Host " $(($htSubscriptionsMgPath.Keys).Count) relevant Subscriptions"
-
- $endEntitiesdata = Get-Date
- Write-Host " Processing Entities data duration: $((New-TimeSpan -Start $startEntitiesdata -End $endEntitiesdata).TotalSeconds) seconds"
-
- $script:arrayEntitiesFromAPISubscriptionsCount = ($arrayEntitiesFromAPI.where( { $_.type -eq '/subscriptions' -and $_.properties.parentNameChain -contains $ManagementGroupId } ) | Sort-Object -Property id -Unique).count
- $script:arrayEntitiesFromAPIManagementGroupsCount = ($arrayEntitiesFromAPI.where( { $_.type -eq 'Microsoft.Management/managementGroups' -and $_.properties.parentNameChain -contains $ManagementGroupId } ) | Sort-Object -Property id -Unique).count + 1
-
- $endEntities = Get-Date
- Write-Host "Processing Entities duration: $((New-TimeSpan -Start $startEntities -End $endEntities).TotalSeconds) seconds"
-}
-function getFileNaming {
- if ($azAPICallConf['htParameters'].onAzureDevOpsOrGitHubActions -eq $true) {
- if ($HierarchyMapOnly) {
- $script:fileName = "AzGovViz_HierarchyMapOnly_$($ManagementGroupId)"
- }
- elseif ($azAPICallConf['htParameters'].ManagementGroupsOnly -eq $true) {
- $script:fileName = "AzGovViz_ManagementGroupsOnly_$($ManagementGroupId)"
- }
- else {
- $script:fileName = "AzGovViz_$($ManagementGroupId)"
- }
- }
- else {
- if ($HierarchyMapOnly) {
- $script:fileName = "AzGovViz_HierarchyMapOnly_$($ProductVersion)_$($fileTimestamp)_$($ManagementGroupId)"
- }
- elseif ($azAPICallConf['htParameters'].ManagementGroupsOnly -eq $true) {
- $script:fileName = "AzGovViz_ManagementGroupsOnly_$($ProductVersion)_$($fileTimestamp)_$($ManagementGroupId)"
- }
- else {
- $script:fileName = "AzGovViz_$($ProductVersion)_$($fileTimestamp)_$($ManagementGroupId)"
- }
- }
-}
-
-function getGroupmembers($aadGroupId, $aadGroupDisplayName) {
- if (-not $htAADGroupsDetails.($aadGroupId)) {
- $script:htAADGroupsDetails.$aadGroupId = @{}
- $script:htAADGroupsDetails.($aadGroupId).Id = $aadGroupId
- $script:htAADGroupsDetails.($aadGroupId).displayname = $aadGroupDisplayName
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/groups/$($aadGroupId)/transitiveMembers"
- $method = 'GET'
- $aadGroupMembers = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask "getGroupmembers $($aadGroupId)"
-
- if ($aadGroupMembers -eq 'Request_ResourceNotFound') {
- $null = $script:arrayGroupRequestResourceNotFound.Add([PSCustomObject]@{
- groupId = $aadGroupId
- })
- }
-
- $aadGroupMembersAll = ($aadGroupMembers)
- $aadGroupMembersUsers = $aadGroupMembers.where( { $_.'@odata.type' -eq '#microsoft.graph.user' } )
- $aadGroupMembersGroups = $aadGroupMembers.where( { $_.'@odata.type' -eq '#microsoft.graph.group' } )
- $aadGroupMembersServicePrincipals = $aadGroupMembers.where( { $_.'@odata.type' -eq '#microsoft.graph.servicePrincipal' } )
-
- $aadGroupMembersAllCount = $aadGroupMembersAll.count
- $aadGroupMembersUsersCount = $aadGroupMembersUsers.count
- $aadGroupMembersGroupsCount = $aadGroupMembersGroups.count
- $aadGroupMembersServicePrincipalsCount = $aadGroupMembersServicePrincipals.count
- #for SP stuff
- if ($aadGroupMembersServicePrincipalsCount -gt 0) {
- foreach ($identity in $aadGroupMembersServicePrincipals) {
- $arrayIdentityObject = [System.Collections.ArrayList]@()
- if ($identity.servicePrincipalType -eq 'Application') {
- if ($identity.appOwnerOrganizationId -eq $azAPICallConf['checkContext'].Tenant.Id) {
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'ServicePrincipal'
- spTypeConcatinated = 'SP APP INT'
- servicePrincipalType = $identity.servicePrincipalType
- id = $identity.id
- appid = $identity.appId
- displayName = $identity.displayName
- appOwnerOrganizationId = $identity.appOwnerOrganizationId
- alternativeNames = $identity.alternativeNames
- })
- }
- else {
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'ServicePrincipal'
- spTypeConcatinated = 'SP APP EXT'
- servicePrincipalType = $identity.servicePrincipalType
- id = $identity.id
- appid = $identity.appId
- displayName = $identity.displayName
- appOwnerOrganizationId = $identity.appOwnerOrganizationId
- alternativeNames = $identity.alternativeNames
- })
- }
- }
- elseif ($identity.servicePrincipalType -eq 'ManagedIdentity') {
- $miType = 'unknown'
- if ($identity.alternativeNames) {
- foreach ($altName in $identity.alternativeNames) {
- if ($altName -like 'isExplicit=*') {
- $splitAltName = $altName.split('=')
- if ($splitAltName[1] -eq 'true') {
- $miType = 'Usr'
- }
- if ($splitAltName[1] -eq 'false') {
- $miType = 'Sys'
- }
- }
- }
- }
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'ServicePrincipal'
- spTypeConcatinated = "SP MI $miType"
- servicePrincipalType = $identity.servicePrincipalType
- id = $identity.id
- appid = $identity.appId
- displayName = $identity.displayName
- appOwnerOrganizationId = $identity.appOwnerOrganizationId
- alternativeNames = $identity.alternativeNames
- })
- }
- else {
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'servicePrincipal'
- spTypeConcatinated = "SP $($identity.servicePrincipalType)"
- servicePrincipalType = $identity.servicePrincipalType
- id = $identity.id
- appid = $identity.appId
- displayName = $identity.displayName
- appOwnerOrganizationId = $identity.appOwnerOrganizationId
- alternativeNames = $identity.alternativeNames
- })
- }
- if (-not $htServicePrincipals.($identity.id)) {
- #Write-Host "$($identity.displayName) $($identity.id) added - - - - - - - - "
- $script:htServicePrincipals.($identity.id) = @{}
- $script:htServicePrincipals.($identity.id) = $arrayIdentityObject
- }
- }
- }
-
- #guests
- if ($aadGroupMembersUsersCount -gt 0) {
- $cntx = 0
- $cnty = 0
- foreach ($aadGroupMembersUser in $aadGroupMembersUsers | Sort-Object -Property id -Unique) {
- $cntx++
- if ($aadGroupMembersUser.userType -eq 'Guest') {
- if (-not $htUserTypesGuest.($aadGroupMembersUser.id)) {
- $cnty++
- #Write-Host "$($aadGroupMembersUser.id) is Guest"
- $script:htUserTypesGuest.($aadGroupMembersUser.id) = @{}
- $script:htUserTypesGuest.($aadGroupMembersUser.id).userType = 'Guest'
- }
- else {
- #Write-Host "$($aadGroupMembersUser.id) already known as Guest"
- }
- }
- }
- }
-
- $script:htAADGroupsDetails.($aadGroupId).MembersAllCount = $aadGroupMembersAllCount
- $script:htAADGroupsDetails.($aadGroupId).MembersUsersCount = $aadGroupMembersUsersCount
- $script:htAADGroupsDetails.($aadGroupId).MembersGroupsCount = $aadGroupMembersGroupsCount
- $script:htAADGroupsDetails.($aadGroupId).MembersServicePrincipalsCount = $aadGroupMembersServicePrincipalsCount
-
- if ($aadGroupMembersAllCount -gt 0) {
- $script:htAADGroupsDetails.($aadGroupId).MembersAll = $aadGroupMembersAll
-
- if ($aadGroupMembersUsersCount -gt 0) {
- $script:htAADGroupsDetails.($aadGroupId).MembersUsers = $aadGroupMembersUsers
- }
- if ($aadGroupMembersGroupsCount -gt 0) {
- $script:htAADGroupsDetails.($aadGroupId).MembersGroups = $aadGroupMembersGroups
- }
- if ($aadGroupMembersServicePrincipalsCount -gt 0) {
- $script:htAADGroupsDetails.($aadGroupId).MembersServicePrincipals = $aadGroupMembersServicePrincipals
- }
- }
- }
-}
-function getMDfCSecureScoreMG {
- $start = Get-Date
- $currentTask = 'Getting Microsoft Defender for Cloud Secure Score for Management Groups'
- Write-Host $currentTask
- #ref: https://docs.microsoft.com/en-us/azure/governance/management-groups/resource-graph-samples?tabs=azure-cli#secure-score-per-management-group
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01"
- $method = 'POST'
-
- $query = @'
- SecurityResources
- | where type == 'microsoft.security/securescores'
- | project subscriptionId,
- subscriptionTotal = iff(properties.score.max == 0, 0.00, round(tolong(properties.weight) * todouble(properties.score.current)/tolong(properties.score.max),2)),
- weight = tolong(iff(properties.weight == 0, 1, properties.weight))
- | join kind=leftouter (
- ResourceContainers
- | where type == 'microsoft.resources/subscriptions' and properties.state == 'Enabled'
- | project subscriptionId, mgChain=properties.managementGroupAncestorsChain )
- on subscriptionId
- | mv-expand mg=mgChain
- | summarize sumSubs = sum(subscriptionTotal), sumWeight = sum(weight), resultsNum = count() by tostring(mg.displayName), mgId = tostring(mg.name)
- | extend secureScore = iff(tolong(resultsNum) == 0, 404.00, round(sumSubs/sumWeight*100,2))
- | project mgDisplayName=mg_displayName, mgId, sumSubs, sumWeight, resultsNum, secureScore
- | order by mgDisplayName asc
-'@
-
- $body = @"
- {
- "query": "$($query)",
- "managementGroups":[
- "$($ManagementGroupId)"
- ]
- }
-"@
-
- $getMgAscSecureScore = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -body $body -listenOn 'Content'
-
- if ($getMgAscSecureScore) {
- if ($getMgAscSecureScore -eq 'capitulation') {
- Write-Host ' Microsoft Defender for Cloud SecureScore for Management Groups will not be available' -ForegroundColor Yellow
- }
- else {
- Write-Host " Retrieved 'Microsoft Defender for Cloud' SecureScore for $($getMgAscSecureScore.Count) Management Groups"
- foreach ($entry in $getMgAscSecureScore) {
- $script:htMgASCSecureScore.($entry.mgId) = @{}
- if ($entry.secureScore -eq 404) {
- $script:htMgASCSecureScore.($entry.mgId).SecureScore = 'n/a'
- }
- else {
- $script:htMgASCSecureScore.($entry.mgId).SecureScore = $entry.secureScore
- }
- }
- }
- }
-
- $end = Get-Date
- Write-Host "Getting Microsoft Defender for Cloud Secure Score for Management Groups duration: $((New-TimeSpan -Start $start -End $end).TotalMinutes) minutes ($((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds)"
-}
-function getOrphanedResources {
- $start = Get-Date
- Write-Host 'Getting orphaned/unused resources (ARG)'
-
- $queries = [System.Collections.ArrayList]@()
- $intent = 'cost savings - stopped but not deallocated VM'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.compute/virtualmachines'
- query = "Resources | where type =~ 'microsoft.compute/virtualmachines' and properties.extended.instanceView.powerState.code =~ 'PowerState/stopped' | project type, subscriptionId, Resource=id, Intent='$intent'"
- intent = $intent
- })
-
- $intent = 'clean up'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.resources/subscriptions/resourceGroups'
- query = "ResourceContainers | where type =~ 'microsoft.resources/subscriptions/resourceGroups' | extend rgAndSub = strcat(resourceGroup, '--', subscriptionId) | join kind=leftouter (Resources | extend rgAndSub = strcat(resourceGroup, '--', subscriptionId) | summarize count() by rgAndSub) on rgAndSub | where isnull(count_) | project type, subscriptionId, Resource=id, Intent='$intent'"
- intent = $intent
- })
-
- $intent = 'misconfiguration'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.network/networkSecurityGroups'
- query = "Resources | where type =~ 'microsoft.network/networkSecurityGroups' and isnull(properties.networkInterfaces) and isnull(properties.subnets) | project type, subscriptionId, Resource=id, Intent='$intent'"
- intent = $intent
- })
-
- $intent = 'misconfiguration'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.network/routeTables'
- query = "resources | where type =~ 'microsoft.network/routeTables' | where isnull(properties.subnets) | project type, subscriptionId, Resource=id, Intent='$intent'"
- intent = $intent
- })
-
- $intent = 'misconfiguration'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.network/networkInterfaces'
- query = "Resources | where type =~ 'microsoft.network/networkInterfaces' | where isnull(properties.privateEndpoint) | where isnull(properties.privateLinkService) | where properties.hostedWorkloads == '[]' | where properties !has 'virtualmachine' | project type, subscriptionId, Resource=id, Intent='$intent'"
- intent = $intent
- })
-
- $intent = 'cost savings'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.compute/disks'
- query = "Resources | where type =~ 'microsoft.compute/disks' | extend diskState = tostring(properties.diskState) | where managedBy == '' | where not(name endswith '-ASRReplica' or name startswith 'ms-asr-' or name startswith 'asrseeddisk-') | project type, subscriptionId, Resource=id, Intent='$intent'"
- intent = $intent
- })
-
- $intent = 'cost savings'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.network/publicIpAddresses'
- query = "Resources | where type =~ 'microsoft.network/publicIpAddresses' | where properties.ipConfiguration == '' and properties.natGateway == '' | project type, subscriptionId, Resource=id, Intent='$intent'"
- intent = $intent
- })
-
- $intent = 'misconfiguration'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.compute/availabilitySets'
- query = "Resources | where type =~ 'microsoft.compute/availabilitySets' | where properties.virtualMachines == '[]' | project type, subscriptionId, Resource=id, Intent='$intent'"
- intent = $intent
- })
-
- $intent = 'misconfiguration'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.network/loadBalancers'
- query = "Resources | where type =~ 'microsoft.network/loadBalancers' | where properties.backendAddressPools == '[]' | project type, subscriptionId, Resource=id, Intent='$intent'"
- intent = $intent
- })
-
- $intent = 'cost savings'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.network/applicationGateways'
- query = "resources | where type =~ 'Microsoft.Network/applicationGateways' | extend backendPoolsCount = array_length(properties.backendAddressPools),SKUName= tostring(properties.sku.name), SKUTier= tostring(properties.sku.tier),SKUCapacity=properties.sku.capacity,backendPools=properties.backendAddressPools | project type, subscriptionId, Resource=id, Intent='$intent' | join (resources | where type =~ 'Microsoft.Network/applicationGateways' | mvexpand backendPools = properties.backendAddressPools | extend backendIPCount = array_length(backendPools.properties.backendIPConfigurations) | extend backendAddressesCount = array_length(backendPools.properties.backendAddresses) | extend backendPoolName = backendPools.properties.backendAddressPools.name | extend Resource = id | summarize backendIPCount = sum(backendIPCount) ,backendAddressesCount=sum(backendAddressesCount) by Resource) on Resource | project-away Resource1 | where (backendIPCount == 0 or isempty(backendIPCount)) and (backendAddressesCount==0 or isempty(backendAddressesCount)) | order by Resource asc"
- intent = $intent
- })
-
- $intent = 'cost savings'
- $null = $queries.Add([PSCustomObject]@{
- queryName = 'microsoft.web/serverfarms'
- query = "Resources | where type =~ 'microsoft.web/serverfarms' | where properties.numberOfSites == 0 | project type, subscriptionId, Resource=id, Intent='$intent'"
- intent = $intent
- })
-
- $queries | ForEach-Object -Parallel {
- $queryDetail = $_
- $arrayOrphanedResources = $using:arrayOrphanedResources
- $subsToProcessInCustomDataCollection = $using:subsToProcessInCustomDataCollection
- $azAPICallConf = $using:azAPICallConf
-
- #Batching: https://docs.microsoft.com/en-us/azure/governance/resource-graph/troubleshoot/general#toomanysubscription
- $counterBatch = [PSCustomObject] @{ Value = 0 }
- $batchSize = 1000
- $subscriptionsBatch = $subsToProcessInCustomDataCollection | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
-
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01"
- $method = 'POST'
- foreach ($batch in $subscriptionsBatch) {
- Write-Host " Getting orphaned $($queryDetail.queryName) for $($batch.Group.subscriptionId.Count) Subscriptions"
- $subscriptions = '"{0}"' -f ($batch.Group.subscriptionId -join '","')
- $body = @"
-{
- "query": "$($queryDetail.query)",
- "subscriptions": [$($subscriptions)],
- "options": {
- "`$top": 1000
- }
-}
-"@
-
- $res = (AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -listenOn 'Content' -currentTask "Getting orphaned $($queryDetail.queryName)")
- #Write-Host '$res.count:' $res.count
- if ($res.count -gt 0) {
- foreach ($resource in $res) {
- $null = $script:arrayOrphanedResources.Add($resource)
- }
- }
- Write-Host " $($res.count) orphaned $($queryDetail.queryName) found"
- }
- } -ThrottleLimit ($queries.Count)
-
- if ($arrayOrphanedResources.Count -gt 0) {
-
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- $allConsumptionDataGroupedByTypeAndCurrency = $allConsumptionData | Group-Object -Property ResourceType, Currency
- $orphanedResourcesResourceTypesCostRelevant = ($queries.where({ $_.intent -like 'cost savings*' })).queryName
-
- $htC = @{}
- foreach ($consumptionResourceTypeAndCurrency in $allConsumptionDataGroupedByTypeAndCurrency) {
- $consumptionResourceTypeAndCurrencySplitted = $consumptionResourceTypeAndCurrency.Name.split(', ')
- if ($consumptionResourceTypeAndCurrencySplitted[0] -in $orphanedResourcesResourceTypesCostRelevant ) {
- foreach ($entry in $consumptionResourceTypeAndCurrency.Group) {
- if (-not $htC.($entry.resourceId)) {
- $htC.($entry.resourceId) = @{}
- $htC.($entry.resourceId).cost = $entry.PreTaxCost
- $htC.($entry.resourceId).currency = $entry.Currency
- }
- else {
- $htC.($entry.resourceId).cost = $htC.($entry.resourceId).cost + $entry.PreTaxCost
- }
- }
- }
- }
-
- $costrelevantOrphanedResourcesGroupedByType = ($arrayOrphanedResources | Group-Object -Property intent).where({ $_.name -like 'cost savings*' }).group | Group-Object -Property type
- $nonCostrelevantOrphanedResourcesGroupedByType = ($arrayOrphanedResources | Group-Object -Property intent).where({ $_.name -notlike 'cost savings*' }).group | Group-Object -Property type
- $script:arrayOrphanedResources = [System.Collections.ArrayList]@()
-
- foreach ($costrelevantOrphanedResourceType in $costrelevantOrphanedResourcesGroupedByType) {
- foreach ($resource in $costrelevantOrphanedResourceType.Group) {
- if ($htC.($resource.Resource)) {
- $null = $script:arrayOrphanedResources.Add([PSCustomObject]@{
- Type = $costrelevantOrphanedResourceType.Name
- Resource = $resource.Resource
- SubscriptionId = $resource.subscriptionId
- Intent = $resource.Intent
- Cost = $htC.($resource.Resource).cost
- Currency = $htC.($resource.Resource).currency
- })
- }
- else {
- $null = $script:arrayOrphanedResources.Add([PSCustomObject]@{
- Type = $costrelevantOrphanedResourceType.Name
- Resource = $resource.Resource
- SubscriptionId = $resource.subscriptionId
- Intent = $resource.Intent
- Cost = ''
- Currency = ''
- })
- }
- }
- }
-
- foreach ($nonCostrelevantOrphanedResourceType in $nonCostrelevantOrphanedResourcesGroupedByType) {
- Write-Host "Processing $($nonCostrelevantOrphanedResourceType.Name)"
- foreach ($resource in $nonCostrelevantOrphanedResourceType.Group) {
- $null = $script:arrayOrphanedResources.Add([PSCustomObject]@{
- Type = $nonCostrelevantOrphanedResourceType.Name
- Resource = $resource.Resource
- SubscriptionId = $resource.subscriptionId
- Intent = $resource.Intent
- Cost = ''
- Currency = ''
- })
- }
- }
- }
-
- Write-Host " Found $($arrayOrphanedResources.Count) orphaned/unused Resources"
- if (-not $NoCsvExport) {
- Write-Host " Exporting OrphanedResources CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName)_ResourcesCostOptimizationAndCleanup.csv'"
- $arrayOrphanedResources | Sort-Object -Property Resource | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_ResourcesCostOptimizationAndCleanup.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
- }
- else {
- Write-Host ' No orphaned/unused Resources found'
- }
-
- $end = Get-Date
- Write-Host "Getting orphaned/unused resources (ARG) processing duration: $((New-TimeSpan -Start $start -End $end).TotalMinutes) minutes ($((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds)"
-}
-function getPIMEligible {
- $start = Get-Date
-
- $currentTask = 'Get PIM onboarded Subscriptions and Management Groups'
- Write-Host $currentTask
- $uriExt = "&`$expand=parent&`$filter=(type eq 'subscription' or type eq 'managementgroup')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/privilegedAccess/azureResources/resources?`$select=id,displayName,type,externalId" + $uriExt
- $res = AzAPICall -AzAPICallConfiguration $azapicallConf -uri $uri -currentTask $currentTask
- if ($res.Count -gt 0) {
-
- $scopesToIterate = [System.Collections.ArrayList]@()
- if (-not $PIMEligibilityIgnoreScope) {
- if (($azAPICallConf['checkContext']).Tenant.Id -ne $ManagementGroupId) {
- foreach ($entry in $res) {
- if ($entry.type -eq 'managementGroup') {
- if ($htManagementGroupsMgPath.($ManagementGroupId).ParentNameChain -contains ($entry.externalId -replace '.*/') -or $htManagementGroupsMgPath.($entry.externalId -replace '.*/').path -contains $ManagementGroupId) {
- $null = $scopesToIterate.Add($entry)
- }
- }
- if ($entry.type -eq 'subscription') {
- if ($htSubscriptionsMgPath.($entry.externalId -replace '.*/').ParentNameChain -contains $ManagementGroupId) {
- if ($htOutOfScopeSubscriptions.($entry.externalId -replace '.*/')) {
- Write-Host "excluding subscription $($entry.externalId -replace '.*/') (outOfScopeSubscription -> $($htOutOfScopeSubscriptions.($entry.externalId -replace '.*/').outOfScopeReason)) (`$PIMEligibilityIgnoreScope=$PIMEligibilityIgnoreScope)"
- }
- else {
- $null = $scopesToIterate.Add($entry)
- }
- }
- }
- }
- }
- else {
- foreach ($entry in $res) {
- if ($htOutOfScopeSubscriptions.($entry.externalId -replace '.*/')) {
- Write-Host "excluding subscription $($entry.externalId -replace '.*/') (outOfScopeSubscription -> $($htOutOfScopeSubscriptions.($entry.externalId -replace '.*/').outOfScopeReason)) (`$PIMEligibilityIgnoreScope=$PIMEligibilityIgnoreScope)"
- }
- else {
- $null = $scopesToIterate.Add($entry)
- }
- }
- }
- }
- else {
- foreach ($entry in $res) {
- $null = $scopesToIterate.Add($entry)
- }
- }
-
- $PIMOnboardedGrouped = $scopesToIterate | Group-Object -Property type
- foreach ($entry in $PIMOnboardedGrouped) {
- Write-Host " Found $($entry.Count) PIM onboarded $($entry.Name)s"
- }
-
- $htPIMEligibleDirect = [System.Collections.Hashtable]::Synchronized((New-Object System.Collections.Hashtable)) #@{}
- $relevantSubscriptionIds = $subsToProcessInCustomDataCollection.subscriptionId
- $scopesToIterate | ForEach-Object -Parallel {
- $scope = $_
- $azAPICallConf = $using:azAPICallConf
- $arrayPIMEligible = $using:arrayPIMEligible
- $htPIMEligibleDirect = $using:htPIMEligibleDirect
- if ($scope.type -eq 'managementgroup') { $htManagementGroupsMgPath = $using:htManagementGroupsMgPath }
- if ($scope.type -eq 'subscription') { $htSubscriptionsMgPath = $using:htSubscriptionsMgPath }
- $htPrincipals = $using:htPrincipals
- $htUserTypesGuest = $using:htUserTypesGuest
- $htServicePrincipals = $using:htServicePrincipals
- $relevantSubscriptionIds = $using:relevantSubscriptionIds
- $function:resolveObjectIds = $using:funcResolveObjectIds
- $function:testGuid = $using:funcTestGuid
-
- $processThisScope = $true
- if ($scope.type -eq 'subscription') {
- if (($scope.externalId -replace '.*/') -notin $relevantSubscriptionIds) {
- Write-Host " Non relevant subscriptionId '$(($scope.externalId -replace '.*/'))' /skipping this subscription as it is not contained in the 'Relevant Subscriptions' collection (needs investigation)" -ForegroundColor DarkRed
- $processThisScope = $false
- }
- }
-
- if ($processThisScope -eq $true) {
- $currentTask = "Get Eligible assignments for Scope $($scope.type): $($scope.externalId -replace '.*/')"
- $extUri = "?`$expand=linkedEligibleRoleAssignment,subject,roleDefinition(`$expand=resource)&`$count=true&`$filter=(roleDefinition/resource/id eq '$($scope.id)')+and+(assignmentState eq 'Eligible')&`$top=100"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/privilegedAccess/azureResources/roleAssignments" + $extUri
- $resx = AzAPICall -AzAPICallConfiguration $azapicallConf -currentTask $currentTask -uri $uri
-
- if ($resx.Count -gt 0) {
-
- $users = $resx.where({ $_.subject.type -eq 'user' })
- if ($users.Count -gt 0) {
- ResolveObjectIds -objectIds $users.subject.id -showActivity
- }
-
- foreach ($entry in $resx) {
- $scopeId = $scope.externalId -replace '.*/'
- if ($scope.type -eq 'managementgroup') {
- $ScopeType = 'MG'
- $ManagementGroupId = $scopeId
- $SubscriptionId = ''
- $SubscriptionDisplayName = ''
- if ($htManagementGroupsMgPath.($scopeId)) {
- $MgDetails = $htManagementGroupsMgPath.($scopeId)
- $ManagementGroupDisplayName = $MgDetails.DisplayName
- $ScopeDisplayName = $MgDetails.DisplayName
- $MgPath = $MgDetails.path
- $MgLevel = $MgDetails.level
- }
- else {
- $ManagementGroupDisplayName = 'notAccessible'
- $ScopeDisplayName = 'notAccessible'
- $MgPath = 'notAccessible'
- $MgLevel = 'notAccessible'
- }
-
- if ($entry.memberType -eq 'direct') {
- $script:htPIMEligibleDirect.($entry.id) = @{}
- $script:htPIMEligibleDirect.($entry.id).clear = $scopeId
- if ($scopeId -eq $ManagementGroupDisplayName) {
- $script:htPIMEligibleDirect.($entry.id).enriched = "$($scopeId) [Level $($MgLevel)]"
- }
- else {
- $script:htPIMEligibleDirect.($entry.id).enriched = "$($ManagementGroupDisplayName) ($($scopeId)) [Level $($MgLevel)]"
- }
- }
- }
- if ($scope.type -eq 'subscription') {
- $ScopeType = 'Sub'
- #$ManagementGroupId = ''
- $SubscriptionId = $scopeId
- if ($htSubscriptionsMgPath.($scopeId)) {
- $MgDetails = $htSubscriptionsMgPath.($scopeId)
- $SubscriptionDisplayName = $MgDetails.DisplayName
- $ScopeDisplayName = $MgDetails.DisplayName
- $MgPath = $MgDetails.path
- $MgLevel = $MgDetails.level
- $ManagementGroupId = $MgDetails.Parent
- $ManagementGroupDisplayName = $MgDetails.ParentName
- }
- else {
- $SubscriptionDisplayName = 'notAccessible'
- $ScopeDisplayName = 'notAccessible'
- $MgPath = 'notAccessible'
- $MgLevel = 'notAccessible'
- }
- #$ManagementGroupDisplayName = ''
-
- }
-
- if ($entry.subject.type -eq 'user') {
- if ($htPrincipals.($entry.subject.id)) {
- $userDetail = $htPrincipals.($entry.subject.id)
- $principalType = "$($userDetail.type) $($userDetail.userType)"
- }
- else {
- $principalType = $entry.subject.type
- }
- }
- else {
- $principalType = $entry.subject.type
- }
-
- $roleType = 'undefined'
- if ($entry.roleDefinition.type -eq 'BuiltInRole') { $roleType = 'Builtin' }
- if ($entry.roleDefinition.type -eq 'CustomRole') { $roleType = 'Custom' }
-
- $null = $script:arrayPIMEligible.Add([PSCustomObject]@{
- ScopeType = $ScopeType
- ScopeId = $scopeId
- ScopeDisplayName = $ScopeDisplayName
- ManagementGroupId = $ManagementGroupId
- ManagementGroupDisplayName = $ManagementGroupDisplayName
- SubscriptionId = $SubscriptionId
- SubscriptionDisplayName = $SubscriptionDisplayName
- MgPath = $MgPath
- MgLevel = $MgLevel
- RoleId = $entry.roleDefinition.externalId
- RoleIdGuid = $entry.roleDefinition.externalId -replace '.*/'
- RoleType = $roleType
- RoleName = $entry.roleDefinition.displayName
- IdentityObjectId = $entry.subject.id
- IdentityType = $principalType
- IdentityDisplayName = $entry.subject.displayName
- IdentityPrincipalName = $entry.subject.principalName
- PIMId = $entry.id
- PIMInheritance = $entry.memberType
- PIMInheritedFromClear = ''
- PIMInheritedFrom = ''
- PIMStartDateTime = $entry.startDateTime
- PIMEndDateTime = $entry.endDateTime
- })
- }
- }
- }
-
- } -ThrottleLimit $ThrottleLimit
-
- foreach ($entry in $arrayPIMEligible) {
- if ($entry.PIMInheritance -eq 'inherited') {
- $entry.PIMInheritedFromClear = $htPIMEligibleDirect.($entry.PIMId).clear
- $entry.PIMInheritedFrom = $htPIMEligibleDirect.($entry.PIMId).enriched
- }
- }
-
- $script:arrayPIMEligibleGrouped = $arrayPIMEligible | Group-Object -Property ScopeType
- foreach ($entry in $arrayPIMEligibleGrouped) {
- Write-Host " Found $($entry.Count) PIM Eligible assignments for $($entry.Name)s"
- }
- }
-
- $end = Get-Date
- Write-Host "Getting PIM Eligible assignments processing duration: $((New-TimeSpan -Start $start -End $end).TotalMinutes) minutes ($((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds)"
-}
-function getPolicyHash {
- [CmdletBinding()]
- param (
- [Parameter(Mandatory)]
- [string]
- $json
- )
- return [System.BitConverter]::ToString([System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($json)))
-}
-function getPolicyRemediation {
- $currentTask = 'Getting NonCompliant (dine/modify)'
- Write-Host $currentTask
- #ref: https://learn.microsoft.com/en-us/rest/api/azureresourcegraph/resourcegraph(2021-03-01)/resources/resources
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01"
- $method = 'POST'
-
- if ($ManagementGroupsOnly) {
- $queryNonCompliant = @'
- policyresources
- | where type == 'microsoft.policyinsights/policystates' and properties.policyAssignmentScope startswith '/providers/Microsoft.Management/managementGroups/' and (properties.policyDefinitionAction =~ 'deployifnotexists' or properties.policyDefinitionAction =~ 'modify') and properties.complianceState =~ 'NonCompliant'
- | summarize count() by assignmentScope = tostring(properties.policyAssignmentScope), assignmentName = tostring(properties.policyAssignmentName), assignmentId = tostring(properties.policyAssignmentId), definitionName = tostring(properties.policyDefinitionName), definitionId = tostring(properties.policyDefinitionId), policyDefinitionReferenceId = tostring(properties.policyDefinitionReferenceId), effect = tostring(properties.policyDefinitionAction)
- | sort by count_, assignmentId, definitionId, policyDefinitionReferenceId, effect
-'@
- }
- else {
- $queryNonCompliant = @'
- policyresources
- | where (properties.policyDefinitionAction =~ 'deployifnotexists' or properties.policyDefinitionAction =~ 'modify') and properties.complianceState =~ 'NonCompliant'
- | summarize count() by assignmentScope = tostring(properties.policyAssignmentScope), assignmentName = tostring(properties.policyAssignmentName), assignmentId = tostring(properties.policyAssignmentId), definitionName = tostring(properties.policyDefinitionName), definitionId = tostring(properties.policyDefinitionId), policyDefinitionReferenceId = tostring(properties.policyDefinitionReferenceId), effect = tostring(properties.policyDefinitionAction)
- | sort by count_, assignmentId, definitionId, policyDefinitionReferenceId, effect
-'@
- }
-
-
- $body = @"
- {
- "query": "$($queryNonCompliant)",
- "managementGroups":[
- "$($ManagementGroupId)"
- ],
- "options": {
- "`$top": 1000
- }
- }
-"@
-
- $getNonCompliant = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -body $body -listenOn 'Content'
- $script:arrayRemediatable = [System.Collections.ArrayList]@()
- Write-Host " Found $($getNonCompliant.Count) remediatable Policy definitions"
- if ($getNonCompliant.Count -gt 0) {
- Write-Host ' Enriching remediatable assignments with displayNames'
- foreach ($nonCompliant in $getNonCompliant) {
-
- if ($htCacheAssignmentsPolicy.($nonCompliant.assignmentId.toLower())) {
- if ($htCacheAssignmentsPolicy.($nonCompliant.assignmentId.toLower()).assignment.properties.policyDefinitionId -like '*/providers/Microsoft.Authorization/policySetDefinitions/*') {
- $policyAssignmentPolicyOrPolicySet = 'policySetDefinition'
- $policySetDefinitionId = $htCacheAssignmentsPolicy.($nonCompliant.assignmentId.toLower()).assignment.properties.policyDefinitionId
- $policySetDefinitionDisplayName = $htCacheDefinitionsPolicySet.($policySetDefinitionId.ToLower()).DisplayName
- $policySetDefinitionName = $policySetDefinitionId -replace '.*/'
- $policySetDefinitionType = $htCacheDefinitionsPolicySet.($policySetDefinitionId.ToLower()).Type
- }
- elseif ($htCacheAssignmentsPolicy.($nonCompliant.assignmentId.toLower()).assignment.properties.policyDefinitionId -like '*/providers/Microsoft.Authorization/policyDefinitions/*') {
- $policyAssignmentPolicyOrPolicySet = 'policyDefinition'
- $policySetDefinitionId = 'n/a'
- $policySetDefinitionDisplayName = 'n/a'
- $policySetDefinitionName = 'n/a'
- $policySetDefinitionType = 'n/a'
- }
- else {
- throw "unexpected .policyDefinitionId: $($htCacheAssignmentsPolicy.($nonCompliant.assignmentId.toLower()).assignment.properties)"
- }
-
- switch ($nonCompliant.assignmentId) {
- { $_ -like '/subscriptions/*' } {
- $policyAssignmentScopeType = 'Sub'
- }
- { $_ -like '/subscriptions/*/resourcegroups/*' } {
- $policyAssignmentScopeType = 'RG'
- }
- { $_ -like '/providers/Microsoft.Management/managementGroups/*' } {
- $policyAssignmentScopeType = 'MG'
- }
- default {
- $policyAssignmentScopeType = 'notDetected'
- }
- }
-
- $null = $script:arrayRemediatable.Add([PSCustomObject]@{
- policyAssignmentScopeType = $policyAssignmentScopeType
- policyAssignmentScope = $nonCompliant.assignmentScope
- policyAssignmentId = $nonCompliant.assignmentId
- policyAssignmentName = $nonCompliant.assignmentName
- policyAssignmentDisplayName = $htCacheAssignmentsPolicy.($nonCompliant.assignmentId.toLower()).assignment.properties.displayName
- policyAssignmentPolicyOrPolicySet = $policyAssignmentPolicyOrPolicySet
- effect = $nonCompliant.effect
- policyDefinitionId = $nonCompliant.definitionId
- policyDefinitionName = $nonCompliant.definitionName
- policyDefinitionDisplayName = $htCacheDefinitionsPolicy.($nonCompliant.definitionId.toLower()).Json.properties.displayName
- policyDefinitionType = $htCacheDefinitionsPolicy.($nonCompliant.definitionId.toLower()).Type
- policySetPolicyDefinitionReferenceId = $nonCompliant.policyDefinitionReferenceId
- policySetDefinitionId = $policySetDefinitionId
- policySetDefinitionName = $policySetDefinitionName
- policySetDefinitionDisplayName = $policySetDefinitionDisplayName
- policySetDefinitionType = $policySetDefinitionType
- nonCompliantResourcesCount = $nonCompliant.count_
- })
- }
- else {
- Write-Host " skipping `$htCacheAssignmentsPolicy.($($nonCompliant.assignmentId)) potentially an assignment on an out-of-scope subscription"
- }
- }
- }
-}
-function getResourceDiagnosticsCapability {
- Write-Host 'Checking Resource Types Diagnostics capability (1st party only)'
- $startResourceDiagnosticsCheck = Get-Date
- if (($resourcesAll).count -gt 0) {
-
- $startGroupResourceIdsByType = Get-Date
- $script:resourceTypesUnique = ($resourcesIdsAll | Group-Object -Property type)
- $endGroupResourceIdsByType = Get-Date
- Write-Host " GroupResourceIdsByType processing duration: $((New-TimeSpan -Start $startGroupResourceIdsByType -End $endGroupResourceIdsByType).TotalSeconds) seconds)"
- $resourceTypesUniqueCount = ($resourceTypesUnique | Measure-Object).count
- Write-Host " $($resourceTypesUniqueCount) unique Resource Types"
- $script:resourceTypesSummarizedArray = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
-
- $script:resourceTypesDiagnosticsArray = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
- $microsoftResourceTypes = $resourceTypesUnique.where({ $_.Name.StartsWith('microsoft') })
- if ($microsoftResourceTypes.Count -gt 0) {
- $microsoftResourceTypes | ForEach-Object -Parallel {
- $resourceTypesUniqueGroup = $_
- $resourcetype = $resourceTypesUniqueGroup.Name
- #region UsingVARs
- #fromOtherFunctions
- $azAPICallConf = $using:azAPICallConf
- $scriptPath = $using:ScriptPath
- #Array&HTs
- $ExcludedResourceTypesDiagnosticsCapable = $using:ExcludedResourceTypesDiagnosticsCapable
- $resourceTypesDiagnosticsArray = $using:resourceTypesDiagnosticsArray
- $htResourceTypesUniqueResource = $using:htResourceTypesUniqueResource
- $resourceTypesSummarizedArray = $using:resourceTypesSummarizedArray
- #endregion UsingVARs
-
- $skipThisResourceType = $false
- if (($ExcludedResourceTypesDiagnosticsCapable).Count -gt 0) {
- foreach ($excludedResourceType in $ExcludedResourceTypesDiagnosticsCapable) {
- if ($excludedResourceType -eq $resourcetype) {
- $skipThisResourceType = $true
- }
- }
- }
-
- if ($skipThisResourceType -eq $false) {
- $resourceCount = $resourceTypesUniqueGroup.Count
-
- #thx @Jim Britt (Microsoft) https://github.com/JimGBritt/AzurePolicy/tree/master/AzureMonitor/Scripts Create-AzDiagPolicy.ps1
- $responseJSON = ''
- $logCategories = @()
- $metrics = $false
- $logs = $false
-
- $resourceAvailability = ($resourceCount - 1)
- $counterTryForResourceType = 0
- do {
- $counterTryForResourceType++
- if ($resourceCount -gt 1) {
- $resourceId = $resourceTypesUniqueGroup.Group.Id[$resourceAvailability]
- }
- else {
- $resourceId = $resourceTypesUniqueGroup.Group.Id
- }
-
- $resourceAvailability = $resourceAvailability - 1
- if ($resourceId -like '*+*') {
- Write-Host "resourceId '$resourceId' contains bad character '+'; skipping resourceId"
- $responseJSON = 'skipResource'
- }
- else {
- $currentTask = "Checking if ResourceType '$resourceType' is capable for Resource Diagnostics using $counterTryForResourceType ResourceId: '$($resourceId)'"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/$($resourceId)/providers/microsoft.insights/diagnosticSettingsCategories?api-version=2021-05-01-preview"
- $method = 'GET'
- $responseJSON = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri ([uri]::EscapeUriString($uri)) -method $method -currentTask $currentTask
- }
-
- if ($responseJSON -ne 'skipResource') {
- if ($responseJSON -eq 'ResourceTypeOrResourceProviderNotSupported') {
- Write-Host " ResourceTypeOrResourceProviderNotSupported | The resource type '$($resourcetype)' does not support diagnostic settings."
-
- }
- else {
- Write-Host " ResourceTypeSupported | The resource type '$($resourcetype)' supports diagnostic settings."
- }
- }
- else {
- Write-Host "resId '$resourceId' skipped"
- }
- }
- until ($resourceAvailability -lt 0 -or $responseJSON -ne 'skipResource')
-
- if ($resourceAvailability -lt 0 -and $responseJSON -eq 'skipResource') {
- Write-Host "tried for all available resourceIds ($($resourceCount)) for resourceType $resourceType, but seems all resourceIds needed to be skipped"
- $null = $script:resourceTypesDiagnosticsArray.Add([PSCustomObject]@{
- ResourceType = $resourcetype
- Metrics = "n/a - $responseJSON"
- Logs = "n/a - $responseJSON"
- LogCategories = 'n/a'
- ResourceCount = $resourceCount
- })
- }
- else {
- if ($responseJSON) {
- foreach ($response in $responseJSON) {
- if ($response.properties.categoryType -eq 'Metrics') {
- $metrics = $true
- }
- if ($response.properties.categoryType -eq 'Logs') {
- $logs = $true
- $logCategories += $response.name
- }
- }
- }
-
- $null = $script:resourceTypesDiagnosticsArray.Add([PSCustomObject]@{
- ResourceType = $resourcetype
- Metrics = $metrics
- Logs = $logs
- LogCategories = $logCategories
- ResourceCount = $resourceCount
- })
- }
- }
- else {
- Write-Host "Skipping ResourceType $($resourcetype) as per parameter '-ExcludedResourceTypesDiagnosticsCapable'"
- }
- } -ThrottleLimit $ThrottleLimit
- }
- else {
- Write-Host ' No 1st party Resource Types at all'
- }
-
- }
- else {
- Write-Host ' No Resources at all'
- }
- $endResourceDiagnosticsCheck = Get-Date
- Write-Host "Checking Resource Types Diagnostics capability duration: $((New-TimeSpan -Start $startResourceDiagnosticsCheck -End $endResourceDiagnosticsCheck).TotalMinutes) minutes ($((New-TimeSpan -Start $startResourceDiagnosticsCheck -End $endResourceDiagnosticsCheck).TotalSeconds) seconds)"
-}
-function getSubscriptions {
- $startGetSubscriptions = Get-Date
- $currentTask = 'Getting all Subscriptions'
- Write-Host "$currentTask"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions?api-version=2020-01-01"
- $method = 'GET'
- $requestAllSubscriptionsAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- $script:htAllSubscriptionsFromAPI = @{}
- $script:htSubscriptionsFromOtherTenants = @{}
-
- Write-Host " $($requestAllSubscriptionsAPI.Count) Subscriptions returned"
- foreach ($subscription in $requestAllSubscriptionsAPI) {
-
- if ($subscription.tenantId -ne $azAPICallConf['checkcontext'].tenant.id) {
- Write-Host " Finding: $($subscription.displayName) ($($subscription.subscriptionId)) belongs to foreign tenant '$($subscription.tenantId)' - Azure Governance Visualizer: excluding this Subscripion" -ForegroundColor DarkRed
- $script:htSubscriptionsFromOtherTenants.($subscription.subscriptionId) = @{}
- $script:htSubscriptionsFromOtherTenants.($subscription.subscriptionId).subDetails = $subscription
- }
- else {
- $script:htAllSubscriptionsFromAPI.($subscription.subscriptionId) = @{}
- $script:htAllSubscriptionsFromAPI.($subscription.subscriptionId).subDetails = $subscription
- }
- }
- Write-Host " $($htAllSubscriptionsFromAPI.Keys.Count) Subscriptions relevant"
-
- $endGetSubscriptions = Get-Date
- Write-Host "Getting all Subscriptions duration: $((New-TimeSpan -Start $startGetSubscriptions -End $endGetSubscriptions).TotalSeconds) seconds"
-}
-function getTenantDetails {
- $currentTask = 'Get Tenant details'
- Write-Host $currentTask
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/tenants?api-version=2020-01-01"
- $method = 'GET'
- $tenantDetailsResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- if (($tenantDetailsResult).count -gt 0) {
- $tenantDetails = $tenantDetailsResult | Where-Object { $_.tenantId -eq ($azAPICallConf['checkContext']).Tenant.Id }
- if ($tenantDetails.displayName) {
- $script:tenantDisplayName = $tenantDetails.displayName
- Write-Host " Tenant DisplayName: $tenantDisplayName"
- }
- else {
- Write-Host ' Tenant DisplayName: could not be retrieved'
- }
-
- if ($tenantDetails.defaultDomain) {
- $script:tenantDefaultDomain = $tenantDetails.defaultDomain
- }
- }
- else {
- Write-Host ' something unexpected'
- }
-}
-function handleCloudEnvironment {
- Write-Host "Environment: $($azAPICallConf['checkContext'].Environment.Name)"
- if ($DoAzureConsumption) {
- if ($azAPICallConf['checkContext'].Environment.Name -eq 'AzureChinaCloud') {
- Write-Host 'Azure Billing not supported in AzureChinaCloud, skipping Consumption..'
- $script:DoAzureConsumption = $false
- }
- }
-}
-function NamingValidation($toCheck) {
- $checks = @(':', '/', '\', '<', '>', '|', '"')
- $array = @()
- foreach ($check in $checks) {
- if ($toCheck -like "*$($check)*") {
- $array += $check
- }
- }
- if ($toCheck -match '\*') {
- $array += '*'
- }
- if ($toCheck -match '\?') {
- $array += '?'
- }
- return $array
-}
-function prepareData {
- Write-Host 'Preparing Data'
- $startPreparingArrays = Get-Date
- $script:optimizedTableForPathQuery = ($newTable | Select-Object -Property level, mg*, subscription*) | Sort-Object -Property level, mgid, subscriptionId -Unique
- $hlperOptimizedTableForPathQuery = $optimizedTableForPathQuery.where( { -not [String]::IsNullOrEmpty($_.SubscriptionId) } )
- $script:optimizedTableForPathQueryMgAndSub = ($hlperOptimizedTableForPathQuery | Select-Object -Property level, mg*, subscription*) | Sort-Object -Property level, mgid, mgname, mgparentId, mgparentName, subscriptionId, subscription -Unique
- $script:optimizedTableForPathQueryMg = ($optimizedTableForPathQuery.where( { [String]::IsNullOrEmpty($_.SubscriptionId) } ) | Select-Object -Property level, mgid, mgName, mgparentid, mgparentName) | Sort-Object -Property level, mgid, mgname, mgparentId, mgparentName -Unique
- $script:optimizedTableForPathQuerySub = ($hlperOptimizedTableForPathQuery | Select-Object -Property subscription*) | Sort-Object -Property subscriptionId -Unique
-
- foreach ($entry in $optimizedTableForPathQuery) {
- $script:htMgDetails.($entry.mgId) = @{}
- $mgSubs = $optimizedTableForPathQueryMgAndSub.where( { $_.mgId -eq $entry.mgId } )
- $script:htMgDetails.($entry.mgId).subscriptionsCount = $mgSubs.Count
- $script:htMgDetails.($entry.mgId).subscriptions = $mgSubs
- $script:htMgDetails.($entry.mgId).details = $entry
- $mgChildren = ($optimizedTableForPathQueryMg.where( { $_.mgParentId -eq $entry.mgId } )).MgId
- $script:htMgDetails.($entry.mgId).mgChildren = $mgChildren
- $script:htMgDetails.($entry.mgId).mgChildrenCount = $mgChildren.Count
- }
-
- foreach ($entry in $optimizedTableForPathQueryMgAndSub) {
- $script:htSubDetails.($entry.SubscriptionId) = @{}
- $script:htSubDetails.($entry.SubscriptionId).details = $optimizedTableForPathQueryMgAndSub.where( { $_.SubscriptionId -eq $entry.SubscriptionId } )
- }
-
- $script:parentMgBaseQuery = ($optimizedTableForPathQueryMg.where( { $_.MgParentId -eq $getMgParentId } ))
- $script:parentMgNamex = $parentMgBaseQuery.mgParentName | Get-Unique
- $script:parentMgIdx = $parentMgBaseQuery.mgParentId | Get-Unique
-
- $endPreparingArrays = Get-Date
- Write-Host "Preparing Arrays duration: $((New-TimeSpan -Start $startPreparingArrays -End $endPreparingArrays).TotalMinutes) minutes ($((New-TimeSpan -Start $startPreparingArrays -End $endPreparingArrays).TotalSeconds) seconds)"
-}
-function processAADGroups {
- if ($NoPIMEligibility) {
- Write-Host 'Resolving AAD Groups (for which a RBAC Role assignment exists)'
- }
- else {
- Write-Host 'Resolving AAD Groups (for which a RBAC Role assignment or PIM Eligibility exists)'
- }
-
- Write-Host " Users known as Guest count: $($htUserTypesGuest.Keys.Count) (before Resolving AAD Groups)"
- $startAADGroupsResolveMembers = Get-Date
-
- $roleAssignmentsforGroups = ($roleAssignmentsUniqueById.where( { $_.RoleAssignmentIdentityObjectType -eq 'Group' } ) | Select-Object -Property RoleAssignmentIdentityObjectId, RoleAssignmentIdentityDisplayname) | Sort-Object -Property RoleAssignmentIdentityObjectId -Unique
- $optimizedTableForAADGroupsQuery = [System.Collections.ArrayList]@()
- if ($roleAssignmentsforGroups.Count -gt 0) {
- foreach ($roleAssignmentforGroups in $roleAssignmentsforGroups) {
- $null = $optimizedTableForAADGroupsQuery.Add($roleAssignmentforGroups)
- }
- }
-
- $aadGroupsCount = ($optimizedTableForAADGroupsQuery).Count
- Write-Host " $aadGroupsCount Groups from RoleAssignments"
-
- if (-not $NoPIMEligibility) {
- $PIMEligibleGroups = $arrayPIMEligible.where({ $_.IdentityType -eq 'Group' }) | Select-Object IdentityObjectId, IdentityDisplayName | Sort-Object -Property IdentityObjectId -Unique
- $cntPIMEligibleGroupsTotal = 0
- $cntPIMEligibleGroupsNotCoveredFromRoleAssignments = 0
- foreach ($PIMEligibleGroup in $PIMEligibleGroups) {
- $cntPIMEligibleGroupsTotal++
- if ($optimizedTableForAADGroupsQuery.RoleAssignmentIdentityObjectId -notcontains $PIMEligibleGroup.IdentityObjectId) {
- $cntPIMEligibleGroupsNotCoveredFromRoleAssignments++
- $null = $optimizedTableForAADGroupsQuery.Add([PSCustomObject]@{
- RoleAssignmentIdentityObjectId = $PIMEligibleGroup.IdentityObjectId
- RoleAssignmentIdentityDisplayname = $PIMEligibleGroup.IdentityDisplayName
- })
- }
- }
- Write-Host " $cntPIMEligibleGroupsTotal Groups from PIM Eligibility; $cntPIMEligibleGroupsNotCoveredFromRoleAssignments Groups added ($($cntPIMEligibleGroupsTotal - $cntPIMEligibleGroupsNotCoveredFromRoleAssignments) already covered in RoleAssignments)"
- $aadGroupsCount = ($optimizedTableForAADGroupsQuery).Count
- Write-Host " $aadGroupsCount Groups from RoleAssignments and PIM Eligibility"
- }
-
- if ($aadGroupsCount -gt 0) {
-
- switch ($aadGroupsCount) {
- { $_ -gt 0 } { $indicator = 1 }
- { $_ -gt 10 } { $indicator = 5 }
- { $_ -gt 50 } { $indicator = 10 }
- { $_ -gt 100 } { $indicator = 20 }
- { $_ -gt 250 } { $indicator = 25 }
- { $_ -gt 500 } { $indicator = 50 }
- { $_ -gt 1000 } { $indicator = 100 }
- { $_ -gt 10000 } { $indicator = 250 }
- }
-
- Write-Host " processing $($aadGroupsCount) AAD Groups (indicating progress in steps of $indicator)"
-
- $optimizedTableForAADGroupsQuery | ForEach-Object -Parallel {
- $aadGroupIdWithRoleAssignment = $_
- #region UsingVARs
- #fromOtherFunctions
- $AADGroupMembersLimit = $using:AADGroupMembersLimit
- $azAPICallConf = $using:azAPICallConf
- $scriptPath = $using:ScriptPath
- #Array&HTs
- $htAADGroupsDetails = $using:htAADGroupsDetails
- $arrayGroupRoleAssignmentsOnServicePrincipals = $using:arrayGroupRoleAssignmentsOnServicePrincipals
- $arrayGroupRequestResourceNotFound = $using:arrayGroupRequestResourceNotFound
- $arrayProgressedAADGroups = $using:arrayProgressedAADGroups
- $htAADGroupsExeedingMemberLimit = $using:htAADGroupsExeedingMemberLimit
- $indicator = $using:indicator
- $htUserTypesGuest = $using:htUserTypesGuest
- $htServicePrincipals = $using:htServicePrincipals
- #other
- $function:getGroupmembers = $using:funcGetGroupmembers
- #endregion UsingVARs
-
- $rndom = Get-Random -Minimum 10 -Maximum 750
- Start-Sleep -Millisecond $rndom
-
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/groups/$($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)/transitiveMembers/`$count"
- $method = 'GET'
- $aadGroupMembersCount = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask "getGroupMembersCountTransitive $($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)" -listenOn 'Content' -consistencyLevel 'eventual'
-
- if ($aadGroupMembersCount -eq 'Request_ResourceNotFound') {
- $null = $script:arrayGroupRequestResourceNotFound.Add([PSCustomObject]@{
- groupId = $aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId
- })
- }
- else {
- if ($aadGroupMembersCount -gt $AADGroupMembersLimit) {
- Write-Host " Group exceeding limit ($($AADGroupMembersLimit)); memberCount: $aadGroupMembersCount; Group: $($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityDisplayname) ($($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)); Members will not be resolved adjust the limit using parameter -AADGroupMembersLimit"
- $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId) = @{}
- $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersAllCount = $aadGroupMembersCount
- $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersUsersCount = 'n/a'
- $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersGroupsCount = 'n/a'
- $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersServicePrincipalsCount = 'n/a'
- }
- else {
- getGroupmembers -aadGroupId $aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId -aadGroupDisplayName $aadGroupIdWithRoleAssignment.RoleAssignmentIdentityDisplayname
- }
- }
-
- $null = $script:arrayProgressedAADGroups.Add($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)
- $processedAADGroupsCount = $null
- $processedAADGroupsCount = ($arrayProgressedAADGroups).Count
- if ($processedAADGroupsCount) {
- if ($processedAADGroupsCount % $indicator -eq 0) {
- Write-Host " $processedAADGroupsCount AAD Groups processed"
- }
- }
- } -ThrottleLimit ($ThrottleLimit * 2)
- }
- else {
- Write-Host " processing $($aadGroupsCount) AAD Groups"
- }
-
- $arrayGroupRequestResourceNotFoundCount = ($arrayGroupRequestResourceNotFound).Count
- if ($arrayGroupRequestResourceNotFoundCount -gt 0) {
- Write-Host "$arrayGroupRequestResourceNotFoundCount Groups could not be checked for Memberships"
- }
-
- Write-Host " processed $($arrayProgressedAADGroups.Count) AAD Groups"
- $endAADGroupsResolveMembers = Get-Date
- Write-Host "Resolving AAD Groups duration: $((New-TimeSpan -Start $startAADGroupsResolveMembers -End $endAADGroupsResolveMembers).TotalMinutes) minutes ($((New-TimeSpan -Start $startAADGroupsResolveMembers -End $endAADGroupsResolveMembers).TotalSeconds) seconds)"
- Write-Host " Users known as Guest count: $($htUserTypesGuest.Keys.Count) (after Resolving AAD Groups)"
-}
-function processALZPolicyVersionChecker {
- $start = Get-Date
- Write-Host "Processing 'Azure Landing Zones (ALZ) Policy Version Checker' base data"
- $ALZRepositoryURI = 'https://github.com/Azure/Enterprise-Scale.git'
- $workingPath = Get-Location
- Write-Host " Working directory is '$($workingPath)'"
- $ALZFolderName = "ALZ_$(Get-Date -Format $FileTimeStampFormat)"
- $ALZPath = "$($OutputPath)/$($ALZFolderName)"
-
- if (-not (Test-Path -LiteralPath "$($ALZPath)")) {
- Write-Host " Creating temporary directory '$($ALZPath)'"
- $null = mkdir $ALZPath
- }
- else {
- Write-Host " Unexpected: The path '$($ALZPath)' already exists"
- throw
- }
-
- Write-Host " Switching to temporary directory '$($ALZPath)'"
- Set-Location $ALZPath
- $ALZCloneSuccess = $false
-
- try {
- Write-Host " Try cloning '$($ALZRepositoryURI)'"
- git clone $ALZRepositoryURI
- if (-not (Test-Path -LiteralPath "$($ALZPath)/Enterprise-Scale" -PathType Container)) {
- $ALZCloneSuccess = $false
- Write-Host " Cloning '$($ALZRepositoryURI)' failed"
- Write-Host " Setting switch parameter '-NoALZPolicyVersionChecker' to true"
- $script:NoALZPolicyVersionChecker = $true
- $script:azAPICallConf['htParameters'].NoALZPolicyVersionChecker = $true
- Write-Host " Switching back to working directory '$($workingPath)'"
- Set-Location $workingPath
- }
- else {
- Write-Host " Cloning '$($ALZRepositoryURI)' succeeded"
- $ALZCloneSuccess = $true
- }
- }
- catch {
- $_
- Write-Host " Cloning '$($ALZRepositoryURI)' failed"
- Write-Host " Setting switch parameter '-NoALZPolicyVersionChecker' to true"
- $script:NoALZPolicyVersionChecker = $true
- $script:azAPICallConf['htParameters'].NoALZPolicyVersionChecker = $true
- Write-Host " Switching back to working directory '$($workingPath)'"
- Set-Location $workingPath
- }
-
- if ($ALZCloneSuccess) {
- Write-Host " Switching to directory '$($ALZPath)/Enterprise-Scale'"
- Set-Location "$($ALZPath)/Enterprise-Scale"
-
- $allESLZPolicies = @{}
- $allESLZPolicySets = @{}
- $allESLZPolicyHashes = @{}
- $allESLZPolicySetHashes = @{}
-
- #Write-Host " Processing ALZ Data Policy definitions"
- $gitHist = (git log --format="%ai`t%H`t%an`t%ae`t%s" -- ./eslzArm/managementGroupTemplates/policyDefinitions/dataPolicies.json) | ConvertFrom-Csv -Delimiter "`t" -Header ('Date', 'CommitId', 'Author', 'Email', 'Subject')
- $commitCount = 0
- $processDataPolicies = $true
- foreach ($commit in $gitHist | Sort-Object -Property Date) {
- if ($processDataPolicies) {
- if ($commit.CommitId -eq '3476914f9ba9a8f3f641a25497dfb24a4efa1017') {
- $processDataPolicies = $false
- continue
- }
- #Write-Host "processing commit (dataPolicies) $($commit.CommitId)"
- $commitCount++
- $jsonRaw = git show "$($commit.CommitId):eslzArm/managementGroupTemplates/policyDefinitions/dataPolicies.json"
-
- $jsonESLZPolicies = $jsonRaw | ConvertFrom-Json
- if (($jsonESLZPolicies.variables.policies.policyDefinitions).Count -eq 0) {
- }
- else {
- $eslzPolicies = $jsonESLZPolicies.variables.policies.policyDefinitions
- foreach ($policyDefinition in $eslzPolicies) {
- $policyJsonConv = ($policyDefinition | ConvertTo-Json -Depth 99) -replace '\[\[', '['
- $policyJsonRebuild = $policyJsonConv | ConvertFrom-Json
- $policyJsonRule = $policyJsonRebuild.properties.policyRule | ConvertTo-Json -Depth 99
- $hash = [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policyJsonRule))
- $stringHash = [System.BitConverter]::ToString($hash)
-
- if (-not $allESLZPolicies.($policyJsonRebuild.name)) {
- $allESLZPolicies.($policyJsonRebuild.name) = @{}
- $allESLZPolicies.($policyJsonRebuild.name).version = [System.Collections.ArrayList]@()
- $null = $allESLZPolicies.($policyJsonRebuild.name).version.Add($policyJsonRebuild.properties.metadata.version)
- $allESLZPolicies.($policyJsonRebuild.name).$stringHash = $policyJsonRebuild.properties.metadata.version
- $allESLZPolicies.($policyJsonRebuild.name).name = $policyJsonRebuild.name
- $allESLZPolicies.($policyJsonRebuild.name).metadataSource = ''
-
- $allESLZPolicies.($policyJsonRebuild.name).status = 'obsolete'
- }
- else {
- $allESLZPolicies.($policyJsonRebuild.name).status = 'obsolete'
-
- if ($allESLZPolicies.($policyJsonRebuild.name).version -notcontains $policyJsonRebuild.properties.metadata.version) {
- $null = $allESLZPolicies.($policyJsonRebuild.name).version.Add($policyJsonRebuild.properties.metadata.version)
- }
- if (-not $allESLZPolicies.($policyJsonRebuild.name).$stringHash) {
- $allESLZPolicies.($policyJsonRebuild.name).$stringHash = $policyJsonRebuild.properties.metadata.version
- }
- }
-
- #hsh
- if (-not $allESLZPolicyHashes.($stringHash)) {
- $allESLZPolicyHashes.($stringHash) = @{}
- $allESLZPolicyHashes.($stringHash).version = [System.Collections.ArrayList]@()
- $null = $allESLZPolicyHashes.($stringHash).version.Add($policyJsonRebuild.properties.metadata.version)
- $allESLZPolicyHashes.($stringHash).name = $policyJsonRebuild.name
- $allESLZPolicyHashes.($stringHash).metadataSource = ''
-
- $allESLZPolicyHashes.($stringHash).status = 'obsolete'
- }
- else {
- $allESLZPolicyHashes.($stringHash).status = 'obsolete'
- if ($allESLZPolicyHashes.($stringHash).version -notcontains $policyJsonRebuild.properties.metadata.version) {
- $null = $allESLZPolicyHashes.($stringHash).version.Add($policyJsonRebuild.properties.metadata.version)
- }
- if (-not $allESLZPolicyHashes.($stringHash).($policyJsonRebuild.name)) {
- $allESLZPolicyHashes.($stringHash).($policyJsonRebuild.name) = $policyJsonRebuild.name
- }
- }
- }
- }
- }
- }
-
- #Write-Host " Processing ALZ Policy and Set definitions"
- $gitHist = (git log --format="%ai`t%H`t%an`t%ae`t%s" -- ./eslzArm/managementGroupTemplates/policyDefinitions/policies.json) | ConvertFrom-Csv -Delimiter "`t" -Header ('Date', 'CommitId', 'Author', 'Email', 'Subject')
- $commitCount = 0
- $doNewALZPolicyReadingApproach = $false
- foreach ($commit in $gitHist | Sort-Object -Property Date) {
-
- if ($commit.CommitId -eq '3476914f9ba9a8f3f641a25497dfb24a4efa1017') {
- $doNewALZPolicyReadingApproach = $true
- }
- #Write-Host "processing commit $($commit.CommitId) - doNewALZPolicyReadingApproach: $doNewALZPolicyReadingApproach"
- $commitCount++
-
- $jsonRaw = git show "$($commit.CommitId):eslzArm/managementGroupTemplates/policyDefinitions/policies.json"
-
- if ($doNewALZPolicyReadingApproach) {
- $jsonESLZPolicies = $jsonRaw -replace '\[\[', '[' | ConvertFrom-Json
- [regex]$extractVariableName = "(?<=\[variables\(')[^']+"
- $refsPolicyDefinitionsAll = $extractVariableName.Matches($jsonESLZPolicies.variables.loadPolicyDefinitions.All).Value
- $refsPolicyDefinitionsAzureCloud = $extractVariableName.Matches($jsonESLZPolicies.variables.loadPolicyDefinitions.AzureCloud).Value
- $refsPolicyDefinitionsAzureChinaCloud = $extractVariableName.Matches($jsonESLZPolicies.variables.loadPolicyDefinitions.AzureChinaCloud).Value
- $refsPolicyDefinitionsAzureUSGovernment = $extractVariableName.Matches($jsonESLZPolicies.variables.loadPolicyDefinitions.AzureUSGovernment).Value
- $refsPolicySetDefinitionsAll = $extractVariableName.Matches($jsonESLZPolicies.variables.loadPolicySetDefinitions.All).Value
- $refsPolicySetDefinitionsAzureCloud = $extractVariableName.Matches($jsonESLZPolicies.variables.loadPolicySetDefinitions.AzureCloud).Value
- $refsPolicySetDefinitionsAzureChinaCloud = $extractVariableName.Matches($jsonESLZPolicies.variables.loadPolicySetDefinitions.AzureChinaCloud).Value
- $refsPolicySetDefinitionsAzureUSGovernment = $extractVariableName.Matches($jsonESLZPolicies.variables.loadPolicySetDefinitions.AzureUSGovernment).Value
- $listPolicyDefinitionsAzureCloud = $refsPolicyDefinitionsAll + $refsPolicyDefinitionsAzureCloud
- $listPolicyDefinitionsAzureChinaCloud = $refsPolicyDefinitionsAll + $refsPolicyDefinitionsAzureChinaCloud
- $listPolicyDefinitionsAzureUSGovernment = $refsPolicyDefinitionsAll + $refsPolicyDefinitionsAzureUSGovernment
- $listPolicySetDefinitionsAzureCloud = $refsPolicySetDefinitionsAll + $refsPolicySetDefinitionsAzureCloud
- $listPolicySetDefinitionsAzureChinaCloud = $refsPolicySetDefinitionsAll + $refsPolicySetDefinitionsAzureChinaCloud
- $listPolicySetDefinitionsAzureUSGovernment = $refsPolicySetDefinitionsAll + $refsPolicySetDefinitionsAzureUSGovernment
- $policyDefinitionsAzureCloud = $listPolicyDefinitionsAzureCloud.ForEach({ $jsonESLZPolicies.variables.$_ })
- $policyDefinitionsAzureChinaCloud = $listPolicyDefinitionsAzureChinaCloud.ForEach({ $jsonESLZPolicies.variables.$_ })
- $policyDefinitionsAzureUSGovernment = $listPolicyDefinitionsAzureUSGovernment.ForEach({ $jsonESLZPolicies.variables.$_ })
- $policySetDefinitionsAzureCloud = $listPolicySetDefinitionsAzureCloud.ForEach({ $jsonESLZPolicies.variables.$_ })
- $policySetDefinitionsAzureChinaCloud = $listPolicySetDefinitionsAzureChinaCloud.ForEach({ $jsonESLZPolicies.variables.$_ })
- $policySetDefinitionsAzureUSGovernment = $listPolicySetDefinitionsAzureUSGovernment.ForEach({ $jsonESLZPolicies.variables.$_ })
-
- switch ($azAPICallConf['checkContext'].Environment.Name) {
- 'Azurecloud' {
- $policyDefinitionsData = $policyDefinitionsAzureCloud
- $policySetDefinitionsData = $policySetDefinitionsAzureCloud
- }
- 'AzureChinaCloud' {
- $policyDefinitionsData = $policyDefinitionsAzureChinaCloud
- $policySetDefinitionsData = $policySetDefinitionsAzureChinaCloud
- }
- 'AzureUSGovernment' {
- $policyDefinitionsData = $policyDefinitionsAzureUSGovernment
- $policySetDefinitionsData = $policySetDefinitionsAzureUSGovernment
- }
- }
-
- foreach ($policyDefinition in $policyDefinitionsData) {
-
- $policyJsonRebuild = $policyDefinition | ConvertFrom-Json
- $policyJsonRule = $policyJsonRebuild.properties.policyRule | ConvertTo-Json -Depth 99
- $hash = [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policyJsonRule))
- $stringHash = [System.BitConverter]::ToString($hash)
-
- if (-not $allESLZPolicies.($policyJsonRebuild.name)) {
- $allESLZPolicies.($policyJsonRebuild.name) = @{}
- $allESLZPolicies.($policyJsonRebuild.name).version = [System.Collections.ArrayList]@()
- $null = $allESLZPolicies.($policyJsonRebuild.name).version.Add($policyJsonRebuild.properties.metadata.version)
- $allESLZPolicies.($policyJsonRebuild.name).$stringHash = $policyJsonRebuild.properties.metadata.version
- $allESLZPolicies.($policyJsonRebuild.name).name = $policyJsonRebuild.name
- $allESLZPolicies.($policyJsonRebuild.name).metadataSource = $policyJsonRebuild.properties.metadata.source
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicies.($policyJsonRebuild.name).status = 'prod'
- }
- else {
- $allESLZPolicies.($policyJsonRebuild.name).status = 'obsolete'
- }
- }
- else {
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicies.($policyJsonRebuild.name).status = 'prod'
- }
- else {
- $allESLZPolicies.($policyJsonRebuild.name).status = 'obsolete'
- }
- $allESLZPolicies.($policyJsonRebuild.name).metadataSource = $policyJsonRebuild.properties.metadata.source
- if ($allESLZPolicies.($policyJsonRebuild.name).version -notcontains $policyJsonRebuild.properties.metadata.version) {
- $null = $allESLZPolicies.($policyJsonRebuild.name).version.Add($policyJsonRebuild.properties.metadata.version)
- }
- if (-not $allESLZPolicies.($policyJsonRebuild.name).$stringHash) {
- $allESLZPolicies.($policyJsonRebuild.name).$stringHash = $policyJsonRebuild.properties.metadata.version
- }
- }
-
- #hsh
- if (-not $allESLZPolicyHashes.($stringHash)) {
- $allESLZPolicyHashes.($stringHash) = @{}
- $allESLZPolicyHashes.($stringHash).version = [System.Collections.ArrayList]@()
- $null = $allESLZPolicyHashes.($stringHash).version.Add($policyJsonRebuild.properties.metadata.version)
- $allESLZPolicyHashes.($stringHash).name = $policyJsonRebuild.name
- $allESLZPolicyHashes.($stringHash).metadataSource = $policyJsonRebuild.properties.metadata.source
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicyHashes.($stringHash).status = 'prod'
- }
- else {
- $allESLZPolicyHashes.($stringHash).status = 'obsolete'
- }
- }
- else {
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicyHashes.($stringHash).status = 'prod'
- }
- else {
- $allESLZPolicyHashes.($stringHash).status = 'obsolete'
- }
- $allESLZPolicyHashes.($stringHash).metadataSource = $policyJsonRebuild.properties.metadata.source
- if ($allESLZPolicyHashes.($stringHash).version -notcontains $policyJsonRebuild.properties.metadata.version) {
- $null = $allESLZPolicyHashes.($stringHash).version.Add($policyJsonRebuild.properties.metadata.version)
- }
- if (-not $allESLZPolicyHashes.($stringHash).($policyJsonRebuild.name)) {
- $allESLZPolicyHashes.($stringHash).($policyJsonRebuild.name) = $policyJsonRebuild.name
- }
- }
- }
-
- foreach ($policySetDefinition in $policySetDefinitionsData) {
-
- $policyJsonRebuild = $policySetDefinition | ConvertFrom-Json
- $policyJsonParameters = $policyJsonRebuild.properties.parameters | ConvertTo-Json -Depth 99
- $policyJsonPolicyDefinitions = $policyJsonRebuild.properties.policyDefinitions | ConvertTo-Json -Depth 99
- $hashParameters = [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policyJsonParameters))
- $stringHashParameters = [System.BitConverter]::ToString($hashParameters)
- $hashPolicyDefinitions = [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policyJsonPolicyDefinitions))
- $stringHashPolicyDefinitions = [System.BitConverter]::ToString($hashPolicyDefinitions)
- $stringHash = "$($stringHashParameters)_$($stringHashPolicyDefinitions)"
-
- if (-not $allESLZPolicySets.($policyJsonRebuild.name)) {
- $allESLZPolicySets.($policyJsonRebuild.name) = @{}
- $allESLZPolicySets.($policyJsonRebuild.name).version = [System.Collections.ArrayList]@()
- $null = $allESLZPolicySets.($policyJsonRebuild.name).version.Add($policyJsonRebuild.properties.metadata.version)
- $allESLZPolicySets.($policyJsonRebuild.name).$stringHash = $policyJsonRebuild.properties.metadata.version
- $allESLZPolicySets.($policyJsonRebuild.name).name = $policyJsonRebuild.name
- $allESLZPolicySets.($policyJsonRebuild.name).metadataSource = $policyJsonRebuild.properties.metadata.source
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicySets.($policyJsonRebuild.name).status = 'prod'
- }
- else {
- $allESLZPolicySets.($policyJsonRebuild.name).status = 'obsolete'
- }
- }
- else {
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicySets.($policyJsonRebuild.name).status = 'prod'
- }
- else {
- $allESLZPolicySets.($policyJsonRebuild.name).status = 'obsolete'
- }
- $allESLZPolicySets.($policyJsonRebuild.name).metadataSource = $policyJsonRebuild.properties.metadata.source
- if ($allESLZPolicySets.($policyJsonRebuild.name).version -notcontains $policyJsonRebuild.properties.metadata.version) {
- $null = $allESLZPolicySets.($policyJsonRebuild.name).version.Add($policyJsonRebuild.properties.metadata.version)
- }
- if (-not $allESLZPolicySets.($policyJsonRebuild.name).$stringHash) {
- $allESLZPolicySets.($policyJsonRebuild.name).$stringHash = $policyJsonRebuild.properties.metadata.version
- }
- }
-
- #hsh
- if (-not $allESLZPolicySetHashes.($stringHash)) {
- $allESLZPolicySetHashes.($stringHash) = @{}
- $allESLZPolicySetHashes.($stringHash).version = [System.Collections.ArrayList]@()
- $null = $allESLZPolicySetHashes.($stringHash).version.Add($policyJsonRebuild.properties.metadata.version)
- $allESLZPolicySetHashes.($stringHash).name = $policyJsonRebuild.name
- $allESLZPolicySetHashes.($stringHash).metadataSource = $policyJsonRebuild.properties.metadata.source
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicySetHashes.($stringHash).status = 'prod'
- }
- else {
- $allESLZPolicySetHashes.($stringHash).status = 'obsolete'
- }
- }
- else {
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicySetHashes.($stringHash).status = 'prod'
- }
- else {
- $allESLZPolicySetHashes.($stringHash).status = 'obsolete'
- }
- $allESLZPolicySetHashes.($stringHash).metadataSource = $policyJsonRebuild.properties.metadata.source
- if ($allESLZPolicySetHashes.($stringHash).version -notcontains $policyJsonRebuild.properties.metadata.version) {
- $null = $allESLZPolicySetHashes.($stringHash).version.Add($policyJsonRebuild.properties.metadata.version)
- }
- if (-not $allESLZPolicySetHashes.($stringHash).($policyJsonRebuild.name)) {
- $allESLZPolicySetHashes.($stringHash).($policyJsonRebuild.name) = $policyJsonRebuild.name
- }
- }
- }
- }
- else {
- $jsonESLZPolicies = $jsonRaw | ConvertFrom-Json
- if (($jsonESLZPolicies.variables.policies.policyDefinitions).Count -eq 0) {
- }
- else {
-
- $eslzPolicies = $jsonESLZPolicies.variables.policies.policyDefinitions
- foreach ($policyDefinition in $eslzPolicies) {
- $policyJsonConv = ($policyDefinition | ConvertTo-Json -Depth 99) -replace '\[\[', '['
- $policyJsonRebuild = $policyJsonConv | ConvertFrom-Json
- $policyJsonRule = $policyJsonRebuild.properties.policyRule | ConvertTo-Json -Depth 99
- $hash = [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policyJsonRule))
- $stringHash = [System.BitConverter]::ToString($hash)
-
- if (-not $allESLZPolicies.($policyJsonRebuild.name)) {
- $allESLZPolicies.($policyJsonRebuild.name) = @{}
- $allESLZPolicies.($policyJsonRebuild.name).version = [System.Collections.ArrayList]@()
- $null = $allESLZPolicies.($policyJsonRebuild.name).version.Add($policyJsonRebuild.properties.metadata.version)
- $allESLZPolicies.($policyJsonRebuild.name).$stringHash = $policyJsonRebuild.properties.metadata.version
- $allESLZPolicies.($policyJsonRebuild.name).name = $policyJsonRebuild.name
- $allESLZPolicies.($policyJsonRebuild.name).metadataSource = ''
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicies.($policyJsonRebuild.name).status = 'prod'
- }
- else {
- $allESLZPolicies.($policyJsonRebuild.name).status = 'obsolete'
- }
- }
- else {
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicies.($policyJsonRebuild.name).status = 'prod'
- }
- else {
- $allESLZPolicies.($policyJsonRebuild.name).status = 'obsolete'
- }
- if ($allESLZPolicies.($policyJsonRebuild.name).version -notcontains $policyJsonRebuild.properties.metadata.version) {
- $null = $allESLZPolicies.($policyJsonRebuild.name).version.Add($policyJsonRebuild.properties.metadata.version)
- }
- if (-not $allESLZPolicies.($policyJsonRebuild.name).$stringHash) {
- $allESLZPolicies.($policyJsonRebuild.name).$stringHash = $policyJsonRebuild.properties.metadata.version
- }
- }
-
- #hsh
- if (-not $allESLZPolicyHashes.($stringHash)) {
- $allESLZPolicyHashes.($stringHash) = @{}
- $allESLZPolicyHashes.($stringHash).version = [System.Collections.ArrayList]@()
- $null = $allESLZPolicyHashes.($stringHash).version.Add($policyJsonRebuild.properties.metadata.version)
- $allESLZPolicyHashes.($stringHash).name = $policyJsonRebuild.name
- $allESLZPolicyHashes.($stringHash).metadataSource = ''
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicyHashes.($stringHash).status = 'prod'
- }
- else {
- $allESLZPolicyHashes.($stringHash).status = 'obsolete'
- }
- }
- else {
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicyHashes.($stringHash).status = 'prod'
- }
- else {
- $allESLZPolicyHashes.($stringHash).status = 'obsolete'
- }
- if ($allESLZPolicyHashes.($stringHash).version -notcontains $policyJsonRebuild.properties.metadata.version) {
- $null = $allESLZPolicyHashes.($stringHash).version.Add($policyJsonRebuild.properties.metadata.version)
- }
- if (-not $allESLZPolicyHashes.($stringHash).($policyJsonRebuild.name)) {
- $allESLZPolicyHashes.($stringHash).($policyJsonRebuild.name) = $policyJsonRebuild.name
- }
- }
- }
-
- $eslzPolicySets = $jsonESLZPolicies.variables.initiatives.policySetDefinitions
- foreach ($policySetDefinition in $eslzPolicySets) {
-
- $policyJsonConv = ($policySetDefinition | ConvertTo-Json -Depth 99) -replace '\[\[', '['
- $policyJsonRebuild = $policyJsonConv | ConvertFrom-Json
- $policyJsonParameters = $policyJsonRebuild.properties.parameters | ConvertTo-Json -Depth 99
- $policyJsonPolicyDefinitions = $policyJsonRebuild.properties.policyDefinitions | ConvertTo-Json -Depth 99
- $hashParameters = [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policyJsonParameters))
- $stringHashParameters = [System.BitConverter]::ToString($hashParameters)
- $hashPolicyDefinitions = [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policyJsonPolicyDefinitions))
- $stringHashPolicyDefinitions = [System.BitConverter]::ToString($hashPolicyDefinitions)
- $stringHash = "$($stringHashParameters)_$($stringHashPolicyDefinitions)"
-
- if (-not $allESLZPolicySets.($policyJsonRebuild.name)) {
- $allESLZPolicySets.($policyJsonRebuild.name) = @{}
- $allESLZPolicySets.($policyJsonRebuild.name).version = [System.Collections.ArrayList]@()
- $null = $allESLZPolicySets.($policyJsonRebuild.name).version.Add($policyJsonRebuild.properties.metadata.version)
- $allESLZPolicySets.($policyJsonRebuild.name).$stringHash = $policyJsonRebuild.properties.metadata.version
- $allESLZPolicySets.($policyJsonRebuild.name).name = $policyJsonRebuild.name
- $allESLZPolicySets.($policyJsonRebuild.name).metadataSource = ''
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicySets.($policyJsonRebuild.name).status = 'prod'
- }
- else {
- $allESLZPolicySets.($policyJsonRebuild.name).status = 'obsolete'
- }
- }
- else {
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicySets.($policyJsonRebuild.name).status = 'prod'
- }
- else {
- $allESLZPolicySets.($policyJsonRebuild.name).status = 'obsolete'
- }
- if ($allESLZPolicySets.($policyJsonRebuild.name).version -notcontains $policyJsonRebuild.properties.metadata.version) {
- $null = $allESLZPolicySets.($policyJsonRebuild.name).version.Add($policyJsonRebuild.properties.metadata.version)
- }
- if (-not $allESLZPolicySets.($policyJsonRebuild.name).$stringHash) {
- $allESLZPolicySets.($policyJsonRebuild.name).$stringHash = $policyJsonRebuild.properties.metadata.version
- }
- }
-
- #hsh
- if (-not $allESLZPolicySetHashes.($stringHash)) {
- $allESLZPolicySetHashes.($stringHash) = @{}
- $allESLZPolicySetHashes.($stringHash).version = [System.Collections.ArrayList]@()
- $null = $allESLZPolicySetHashes.($stringHash).version.Add($policyJsonRebuild.properties.metadata.version)
- $allESLZPolicySetHashes.($stringHash).name = $policyJsonRebuild.name
- $allESLZPolicySetHashes.($stringHash).metadataSource = ''
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicySetHashes.($stringHash).status = 'prod'
- }
- else {
- $allESLZPolicySetHashes.($stringHash).status = 'obsolete'
- }
- }
- else {
- if ($commitCount -eq $gitHist.Count) {
- $allESLZPolicySetHashes.($stringHash).status = 'prod'
- }
- else {
- $allESLZPolicySetHashes.($stringHash).status = 'obsolete'
- }
- if ($allESLZPolicySetHashes.($stringHash).version -notcontains $policyJsonRebuild.properties.metadata.version) {
- $null = $allESLZPolicySetHashes.($stringHash).version.Add($policyJsonRebuild.properties.metadata.version)
- }
- if (-not $allESLZPolicySetHashes.($stringHash).($policyJsonRebuild.name)) {
- $allESLZPolicySetHashes.($stringHash).($policyJsonRebuild.name) = $policyJsonRebuild.name
- }
- }
- }
- }
- }
- }
-
-
- Write-Host " $($allESLZPolicies.Keys.Count) Azure Landing Zones (ALZ) Policy definitions ($($allESLZPolicies.Values.where({$_.status -eq 'Prod'}).Count) productive)"
- Write-Host " $($allESLZPolicySets.Keys.Count) Azure Landing Zones (ALZ) PolicySet definitions ($($allESLZPolicySets.Values.where({$_.status -eq 'Prod'}).Count) productive)"
-
- $arrayObsoleteALZPolicies = @(
- 'Deny-PublicEndpoint-Aks',
- 'Deny-PublicEndpoint-CosmosDB',
- 'Deny-PublicEndpoint-KeyVault',
- 'Deny-PublicEndpoint-MySQL',
- 'Deny-PublicEndpoint-PostgreSql',
- 'Deny-PublicEndpoint-Sql',
- 'Deny-PublicEndpoint-Storage',
- 'Deploy-ASC-Standard',
- 'Deploy-Diagnostics-ActivityLog',
- 'Deploy-Diagnostics-AKS',
- 'Deploy-Diagnostics-Batch',
- 'Deploy-Diagnostics-DataLakeStore',
- 'Deploy-Diagnostics-EventHub',
- 'Deploy-Diagnostics-KeyVault',
- 'Deploy-Diagnostics-LogicAppsWF',
- 'Deploy-Diagnostics-PublicIP',
- 'Deploy-Diagnostics-RecoveryVault',
- 'Deploy-Diagnostics-SearchServices',
- 'Deploy-Diagnostics-ServiceBus',
- 'Deploy-Diagnostics-SQLDBs',
- 'Deploy-Diagnostics-StreamAnalytics',
- 'Deploy-DNSZoneGroup-For-Blob-PrivateEndpoint',
- 'Deploy-DNSZoneGroup-For-File-PrivateEndpoint',
- 'Deploy-DNSZoneGroup-For-KeyVault-PrivateEndpoint',
- 'Deploy-DNSZoneGroup-For-Queue-PrivateEndpoint',
- 'Deploy-DNSZoneGroup-For-Sql-PrivateEndpoint',
- 'Deploy-DNSZoneGroup-For-Table-PrivateEndpoint',
- 'Deploy-HUB',
- 'Deploy-LA-Config',
- 'Deploy-Log-Analytics',
- 'Deploy-vHUB',
- 'Deploy-vNet',
- 'Deploy-vWAN'
- )
- foreach ($obsoleteALZPolicy in $arrayObsoleteALZPolicies) {
- if (-not $alzPolicies.($obsoleteALZPolicy)) {
- $script:alzPolicies.($obsoleteALZPolicy) = @{}
- $script:alzPolicies.($obsoleteALZPolicy).latestVersion = ''
- $script:alzPolicies.($obsoleteALZPolicy).status = 'obsolete'
- $script:alzPolicies.($obsoleteALZPolicy).policyName = $obsoleteALZPolicy
- $script:alzPolicies.($obsoleteALZPolicy).metadataSource = ''
- }
- }
-
- foreach ($entry in $allESLZPolicies.keys | Sort-Object) {
- $thisOne = $allESLZPolicies.($entry)
- $latestVersion = ([array]($thisOne.version | Sort-Object -Descending))[0]
- $script:alzPolicies.($entry) = @{}
- $script:alzPolicies.($entry).latestVersion = $latestVersion
- $script:alzPolicies.($entry).status = $thisOne.status
- $script:alzPolicies.($entry).policyName = $thisOne.name
- $script:alzPolicies.($entry).metadataSource = $thisOne.name
- }
-
- foreach ($entry in $allESLZPolicyHashes.keys | Sort-Object) {
- $thisOne = $allESLZPolicyHashes.($entry)
- $latestVersion = ([array]($thisOne.version | Sort-Object -Descending))[0]
- $script:alzPolicyHashes.($entry) = @{}
- $script:alzPolicyHashes.($entry).latestVersion = $latestVersion
- $script:alzPolicyHashes.($entry).status = $thisOne.status
- $script:alzPolicyHashes.($entry).policyName = $thisOne.name
- $script:alzPolicyHashes.($entry).metadataSource = $thisOne.metadataSource
- }
-
- $script:alzPolicySets.'Deploy-Diag-LogAnalytics' = @{}
- $script:alzPolicySets.'Deploy-Diag-LogAnalytics'.latestVersion = '1.0.0'
- $script:alzPolicySets.'Deploy-Diag-LogAnalytics'.status = 'obsolete'
- $script:alzPolicySets.'Deploy-Diag-LogAnalytics'.policySetName = 'Deploy-Diag-LogAnalytics'
- foreach ($entry in $allESLZPolicySets.keys | Sort-Object) {
- $thisOne = $allESLZPolicySets.($entry)
- $latestVersion = ([array]($thisOne.version | Sort-Object -Descending))[0]
- $script:alzPolicySets.($entry) = @{}
- $script:alzPolicySets.($entry).latestVersion = $latestVersion
- $script:alzPolicySets.($entry).status = $thisOne.status
- $script:alzPolicySets.($entry).policySetName = $thisOne.name
- $script:alzPolicySets.($entry).metadataSource = $thisOne.metadataSource
- }
-
- foreach ($entry in $allESLZPolicySetHashes.keys | Sort-Object) {
- $thisOne = $allESLZPolicySetHashes.($entry)
- $latestVersion = ([array]($thisOne.version | Sort-Object -Descending))[0]
- $script:alzPolicySetHashes.($entry) = @{}
- $script:alzPolicySetHashes.($entry).latestVersion = $latestVersion
- $script:alzPolicySetHashes.($entry).status = $thisOne.status
- $script:alzPolicySetHashes.($entry).policySetName = $thisOne.name
- $script:alzPolicySetHashes.($entry).metadataSource = $thisOne.metadataSource
- }
-
- Write-Host " Switching back to working directory '$($workingPath)'"
- Set-Location $workingPath
-
- Write-Host " Removing temporary directory '$($ALZPath)'"
- Remove-Item -Recurse -Force $ALZPath
- }
-
- $end = Get-Date
- Write-Host " Processing 'Azure Landing Zones (ALZ) Policy Version Checker' base data duration: $((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds"
-}
-function processApplications {
- Write-Host 'Processing Service Principals - Applications'
- $script:servicePrincipalsOfTypeApplication = $htServicePrincipals.Keys.where( { $htServicePrincipals.($_).servicePrincipalType -eq 'Application' -and $htServicePrincipals.($_).appOwnerOrganizationId -eq $azAPICallConf['checkContext'].Subscription.TenantId } )
- if ($azAPICallConf['htParameters'].userType -eq 'Guest') {
- #checking if Guest has enough permissions
- $app4Test = $htServicePrincipals.($servicePrincipalsOfTypeApplication[0])
- $currentTask = "getApp Test $($app4Test.appId)"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications?`$filter=appId eq '$($app4Test.appId)'"
- $method = 'GET'
- $testGetApplication = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
- if ($testGetApplication -eq 'skipApplications') {
- $skipApplications = $true
- Write-Host ' Guest account does not have enough permissions, skipping Applications (Secrets & Certificates)'
- }
- }
- if (-not $skipApplications) {
- $startSPApp = Get-Date
- $currentDateUTC = (Get-Date).ToUniversalTime()
- $script:arrayApplicationRequestResourceNotFound = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
- $servicePrincipalsOfTypeApplication | ForEach-Object -Parallel {
-
- #region UsingVARs
- $currentDateUTC = $using:currentDateUTC
- #fromOtherFunctions
- $azAPICallConf = $using:azAPICallConf
- $scriptPath = $using:ScriptPath
- #Array&HTs
- $arrayApplicationRequestResourceNotFound = $using:arrayApplicationRequestResourceNotFound
- $htAppDetails = $using:htAppDetails
- $htServicePrincipals = $using:htServicePrincipals
- #endregion UsingVARs
-
- $sp = $htServicePrincipals.($_)
-
- $currentTask = "getApp $($sp.appId)"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications?`$filter=appId eq '$($sp.appId)'"
- $method = 'GET'
- $getApplication = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- if ($getApplication -eq 'Request_ResourceNotFound') {
- $null = $script:arrayApplicationRequestResourceNotFound.Add([PSCustomObject]@{
- appId = $sp.appId
- })
- }
- else {
- if (($getApplication).Count -eq 0) {
- Write-Host "$($sp.appId) no data returned / seems non existent?"
- }
- else {
- $script:htAppDetails.($sp.id) = @{}
- $script:htAppDetails.($sp.id).servicePrincipalType = $sp.servicePrincipalType
- $script:htAppDetails.($sp.id).spGraphDetails = $sp
- $script:htAppDetails.($sp.id).appGraphDetails = $getApplication
-
- $appPasswordCredentialsCount = ($getApplication.passwordCredentials).count
- if ($appPasswordCredentialsCount -gt 0) {
- $script:htAppDetails.($sp.id).appPasswordCredentialsCount = $appPasswordCredentialsCount
- $appPasswordCredentialsExpiredCount = 0
- $appPasswordCredentialsGracePeriodExpiryCount = 0
- $appPasswordCredentialsExpiryOKCount = 0
- $appPasswordCredentialsExpiryOKMoreThan2YearsCount = 0
- foreach ($appPasswordCredential in $getApplication.passwordCredentials) {
- $passwordExpiryTotalDays = (New-TimeSpan -Start $currentDateUTC -End $appPasswordCredential.endDateTime).TotalDays
- if ($passwordExpiryTotalDays -lt 0) {
- $appPasswordCredentialsExpiredCount++
- }
- elseif ($passwordExpiryTotalDays -lt $AADServicePrincipalExpiryWarningDays) {
- $appPasswordCredentialsGracePeriodExpiryCount++
- }
- else {
- if ($passwordExpiryTotalDays -gt 730) {
- $appPasswordCredentialsExpiryOKMoreThan2YearsCount++
- }
- else {
- $appPasswordCredentialsExpiryOKCount++
- }
- }
- }
- $script:htAppDetails.($sp.id).appPasswordCredentialsExpiredCount = $appPasswordCredentialsExpiredCount
- $script:htAppDetails.($sp.id).appPasswordCredentialsGracePeriodExpiryCount = $appPasswordCredentialsGracePeriodExpiryCount
- $script:htAppDetails.($sp.id).appPasswordCredentialsExpiryOKCount = $appPasswordCredentialsExpiryOKCount
- $script:htAppDetails.($sp.id).appPasswordCredentialsExpiryOKMoreThan2YearsCount = $appPasswordCredentialsExpiryOKMoreThan2YearsCount
- }
-
- $appKeyCredentialsCount = ($getApplication.keyCredentials).count
- if ($appKeyCredentialsCount -gt 0) {
- $script:htAppDetails.($sp.id).appKeyCredentialsCount = $appKeyCredentialsCount
- $appKeyCredentialsExpiredCount = 0
- $appKeyCredentialsGracePeriodExpiryCount = 0
- $appKeyCredentialsExpiryOKCount = 0
- $appKeyCredentialsExpiryOKMoreThan2YearsCount = 0
- foreach ($appKeyCredential in $getApplication.keyCredentials) {
- $keyCredentialExpiryTotalDays = (New-TimeSpan -Start $currentDateUTC -End $appKeyCredential.endDateTime).TotalDays
- if ($keyCredentialExpiryTotalDays -lt 0) {
- $appKeyCredentialsExpiredCount++
- }
- elseif ($keyCredentialExpiryTotalDays -lt $AADServicePrincipalExpiryWarningDays) {
- $appKeyCredentialsGracePeriodExpiryCount++
- }
- else {
- if ($keyCredentialExpiryTotalDays -gt 730) {
- $appKeyCredentialsExpiryOKMoreThan2YearsCount++
- }
- else {
- $appKeyCredentialsExpiryOKCount++
- }
- }
- }
- $script:htAppDetails.($sp.id).appKeyCredentialsExpiredCount = $appKeyCredentialsExpiredCount
- $script:htAppDetails.($sp.id).appKeyCredentialsGracePeriodExpiryCount = $appKeyCredentialsGracePeriodExpiryCount
- $script:htAppDetails.($sp.id).appKeyCredentialsExpiryOKCount = $appKeyCredentialsExpiryOKCount
- $script:htAppDetails.($sp.id).appKeyCredentialsExpiryOKMoreThan2YearsCount = $appKeyCredentialsExpiryOKMoreThan2YearsCount
- }
- }
- }
-
- } -ThrottleLimit ($ThrottleLimit * 2)
-
- $endSPApp = Get-Date
- Write-Host "Processing Service Principals - Applications duration: $((New-TimeSpan -Start $startSPApp -End $endSPApp).TotalMinutes) minutes ($((New-TimeSpan -Start $startSPApp -End $endSPApp).TotalSeconds) seconds)"
- }
-}
-function processDataCollection {
- [CmdletBinding()]Param(
- [string]$mgId
- )
-
- Write-Host ' CustomDataCollection ManagementGroups'
- $startMgLoop = Get-Date
-
- $allManagementGroupsFromEntitiesChildOfRequestedMg = $arrayEntitiesFromAPI.where( { $_.type -eq 'Microsoft.Management/managementGroups' -and ($_.Name -eq $mgId -or $_.properties.parentNameChain -contains $mgId) })
- $allManagementGroupsFromEntitiesChildOfRequestedMgCount = ($allManagementGroupsFromEntitiesChildOfRequestedMg).Count
- Write-Host " $allManagementGroupsFromEntitiesChildOfRequestedMgCount Management Groups: $(($allManagementGroupsFromEntitiesChildOfRequestedMg.Name | Sort-Object) -join ', ')"
- $mgBatch = ($allManagementGroupsFromEntitiesChildOfRequestedMg | Group-Object -Property { ($_.properties.parentNameChain).Count }) | Sort-Object -Property Name
- Write-Host " $(($mgBatch | Measure-Object).Count) batches of Management Groups to process:"
-
- $btchCnt = 0
- foreach ($btch in $mgBatch) {
- $btchCnt++
- $listOfMGs = @()
- foreach ($btchMg in $btch.Group | Sort-Object -Property name) {
- if ($btchMg.name -eq $btchMg.Properties.displayName) {
- $listOfMGs += $btchMg.name
- }
- else {
- $listOfMGs += "$($btchMg.name) ($($btchMg.Properties.displayName))"
- }
- }
- Write-Host " Batch#$($btchCnt) - $($listOfMGs.Count) Management Groups: $($listOfMGs -join ', ')"
- }
-
- foreach ($batchLevel in $mgBatch) {
- Write-Host " Processing Management Groups L$($batchLevel.Name) ($($batchLevel.Count) Management Groups)"
-
- showMemoryUsage
-
- $batchLevel.Group | ForEach-Object -Parallel {
- $mgdetail = $_
- #region UsingVARs
- #Parameters MG&Sub related
- $CsvDelimiter = $using:CsvDelimiter
- $CsvDelimiterOpposite = $using:CsvDelimiterOpposite
- $ManagementGroupId = $using:ManagementGroupId
- #fromOtherFunctions
- $azAPICallConf = $using:azAPICallConf
- $scriptPath = $using:ScriptPath
- #Array&HTs
- $newTable = $using:newTable
- $customDataCollectionDuration = $using:customDataCollectionDuration
- $htSubscriptionTagList = $using:htSubscriptionTagList
- $htResourceTypesUniqueResource = $using:htResourceTypesUniqueResource
- $htAllTagList = $using:htAllTagList
- $htSubscriptionTags = $using:htSubscriptionTags
- $htCacheDefinitionsPolicy = $using:htCacheDefinitionsPolicy
- $htCacheDefinitionsPolicySet = $using:htCacheDefinitionsPolicySet
- $htCacheDefinitionsRole = $using:htCacheDefinitionsRole
- $htCacheDefinitionsBlueprint = $using:htCacheDefinitionsBlueprint
- $htRoleDefinitionIdsUsedInPolicy = $using:htRoleDefinitionIdsUsedInPolicy
- $htCachePolicyComplianceMG = $using:htCachePolicyComplianceMG
- $htCachePolicyComplianceResponseTooLargeMG = $using:htCachePolicyComplianceResponseTooLargeMG
- $htCacheAssignmentsRole = $using:htCacheAssignmentsRole
- $htCacheAssignmentsRBACOnResourceGroupsAndResources = $using:htCacheAssignmentsRBACOnResourceGroupsAndResources
- $htCacheAssignmentsBlueprint = $using:htCacheAssignmentsBlueprint
- $htCacheAssignmentsPolicy = $using:htCacheAssignmentsPolicy
- $htPolicyAssignmentExemptions = $using:htPolicyAssignmentExemptions
- $htManagementGroupsMgPath = $using:htManagementGroupsMgPath
- $LimitPOLICYPolicyDefinitionsScopedManagementGroup = $using:LimitPOLICYPolicyDefinitionsScopedManagementGroup
- $LimitPOLICYPolicySetDefinitionsScopedManagementGroup = $using:LimitPOLICYPolicySetDefinitionsScopedManagementGroup
- $LimitPOLICYPolicyAssignmentsManagementGroup = $using:LimitPOLICYPolicyAssignmentsManagementGroup
- $LimitPOLICYPolicySetAssignmentsManagementGroup = $using:LimitPOLICYPolicySetAssignmentsManagementGroup
- $LimitRBACRoleAssignmentsManagementGroup = $using:LimitRBACRoleAssignmentsManagementGroup
- $arrayEntitiesFromAPI = $using:arrayEntitiesFromAPI
- $allManagementGroupsFromEntitiesChildOfRequestedMgCount = $using:allManagementGroupsFromEntitiesChildOfRequestedMgCount
- $arrayDataCollectionProgressMg = $using:arrayDataCollectionProgressMg
- $arrayDiagnosticSettingsMgSub = $using:arrayDiagnosticSettingsMgSub
- $htMgAtScopePolicyAssignments = $using:htMgAtScopePolicyAssignments
- $htMgAtScopePoliciesScoped = $using:htMgAtScopePoliciesScoped
- $htMgAtScopeRoleAssignments = $using:htMgAtScopeRoleAssignments
- $htMgASCSecureScore = $using:htMgASCSecureScore
- $htRoleAssignmentsFromAPIInheritancePrevention = $using:htRoleAssignmentsFromAPIInheritancePrevention
- $htNamingValidation = $using:htNamingValidation
- $htPrincipals = $using:htPrincipals
- $htServicePrincipals = $using:htServicePrincipals
- $htUserTypesGuest = $using:htUserTypesGuest
- $htRoleAssignmentsPIM = $using:htRoleAssignmentsPIM
- $alzPolicies = $using:alzPolicies
- $alzPolicySets = $using:alzPolicySets
- $alzPolicyHashes = $using:alzPolicyHashes
- $alzPolicySetHashes = $using:alzPolicySetHashes
- $htDoARMRoleAssignmentScheduleInstances = $using:htDoARMRoleAssignmentScheduleInstances
- $ValidPolicyEffects = $using:ValidPolicyEffects
- #other
- $function:addRowToTable = $using:funcAddRowToTable
- $function:namingValidation = $using:funcNamingValidation
- $function:resolveObjectIds = $using:funcResolveObjectIds
- $function:testGuid = $using:funcTestGuid
-
- $function:dataCollectionMGSecureScore = $using:funcDataCollectionMGSecureScore
- $function:dataCollectionDiagnosticsMG = $using:funcDataCollectionDiagnosticsMG
- $function:dataCollectionPolicyComplianceStates = $using:funcDataCollectionPolicyComplianceStates
- $function:dataCollectionBluePrintDefinitionsMG = $using:funcDataCollectionBluePrintDefinitionsMG
- $function:dataCollectionPolicyExemptions = $using:funcDataCollectionPolicyExemptions
- $function:dataCollectionPolicyDefinitions = $using:funcDataCollectionPolicyDefinitions
- $function:dataCollectionPolicySetDefinitions = $using:funcDataCollectionPolicySetDefinitions
- $function:dataCollectionPolicyAssignmentsMG = $using:funcDataCollectionPolicyAssignmentsMG
- $function:dataCollectionRoleDefinitions = $using:funcDataCollectionRoleDefinitions
- $function:dataCollectionRoleAssignmentsMG = $using:funcDataCollectionRoleAssignmentsMG
- $function:detectPolicyEffect = $using:funcDetectPolicyEffect
-
- #endregion usingVARS
- $builtInPolicyDefinitionsCount = $using:builtInPolicyDefinitionsCount
-
- $addRowToTableDone = $false
-
- $MgDetailThis = $htManagementGroupsMgPath.($mgdetail.Name)
- $MgParentId = $MgDetailThis.Parent
- $hierarchyLevel = $MgDetailThis.ParentNameChainCount
-
- if ($MgParentId -eq '__TenantRoot__') {
- $MgParentId = 'TenantRoot'
- $MgParentName = $MgParentId
- }
- else {
- $MgParentName = $htManagementGroupsMgPath.($MgParentId).DisplayName
- }
-
- $rndom = Get-Random -Minimum 10 -Maximum 750
- Start-Sleep -Millisecond $rndom
- $startMgLoopThis = Get-Date
-
- if ($azAPICallConf['htParameters'].HierarchyMapOnly -eq $false) {
-
- #namingValidation
- if (-not [string]::IsNullOrEmpty($mgdetail.properties.displayName)) {
- $namingValidationResult = NamingValidation -toCheck $mgdetail.properties.displayName
- if ($namingValidationResult.Count -gt 0) {
- $script:htNamingValidation.ManagementGroup.($mgdetail.Name) = @{}
- $script:htNamingValidation.ManagementGroup.($mgdetail.Name).nameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.ManagementGroup.($mgdetail.Name).name = $mgdetail.properties.displayName
- }
- }
-
- $targetMgOrSub = 'MG'
- $baseParameters = @{
- scopeId = $mgdetail.Name
- scopeDisplayName = $mgdetail.properties.displayName
- }
-
- #ManagementGroupASCSecureScore
- $mgAscSecureScoreResult = DataCollectionMGSecureScore -Id $mgdetail.Name
-
- $addRowToTableParameters = @{
- hierarchyLevel = $hierarchyLevel
- mgParentId = $mgParentId
- mgParentName = $mgParentName
- mgAscSecureScoreResult = $mgAscSecureScoreResult
- }
-
- #mg diag
- DataCollectionDiagnosticsMG @baseParameters
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- #MGPolicyCompliance
- DataCollectionPolicyComplianceStates @baseParameters -TargetMgOrSub $targetMgOrSub
- }
-
- #MGBlueprintDefinitions
- $functionReturn = DataCollectionBluePrintDefinitionsMG @baseParameters @addRowToTableParameters
- if ($functionReturn.'addRowToTableDone') {
- $addRowToTableDone = $true
- }
-
- #MGPolicyExemptions
- DataCollectionPolicyExemptions @baseParameters -TargetMgOrSub $targetMgOrSub
-
- #MGPolicyDefinitions
- $functionReturn = DataCollectionPolicyDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
- $policyDefinitionsScopedCount = $functionReturn.'PolicyDefinitionsScopedCount'
-
- #MGPolicySetDefinitions
- $functionReturn = DataCollectionPolicySetDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
- $policySetDefinitionsScopedCount = $functionReturn.'PolicySetDefinitionsScopedCount'
-
- if (-not $htMgAtScopePoliciesScoped.($mgdetail.Name)) {
- $script:htMgAtScopePoliciesScoped.($mgdetail.Name) = @{}
- $script:htMgAtScopePoliciesScoped.($mgdetail.Name).ScopedCount = $policyDefinitionsScopedCount + $policySetDefinitionsScopedCount
- }
-
- $scopedPolicyCounts = @{
- policyDefinitionsScopedCount = $policyDefinitionsScopedCount
- policySetDefinitionsScopedCount = $policySetDefinitionsScopedCount
- }
-
- #MgPolicyAssignments
- $functionReturn = DataCollectionPolicyAssignmentsMG @baseParameters @addRowToTableParameters @scopedPolicyCounts
- if ($functionReturn.'addRowToTableDone') {
- $addRowToTableDone = $true
- }
-
- #MGRoleDefinitions
- DataCollectionRoleDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
-
- #MGRoleAssignments
- $functionReturn = DataCollectionRoleAssignmentsMG @baseParameters @addRowToTableParameters
- if ($functionReturn.'addRowToTableDone') {
- $addRowToTableDone = $true
- }
-
- if ($addRowToTableDone -ne $true) {
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $mgdetail.properties.displayName `
- -mgId $mgdetail.Name `
- -mgParentId $mgParentId `
- -mgParentName $mgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult
- }
- }
- else {
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $mgdetail.properties.displayName `
- -mgId $mgdetail.Name `
- -mgParentId $mgParentId `
- -mgParentName $mgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult
- }
-
-
- $endMgLoopThis = Get-Date
- $null = $script:customDataCollectionDuration.Add([PSCustomObject]@{
- Type = 'Mg'
- Id = $mgdetail.Name
- DurationSec = (New-TimeSpan -Start $startMgLoopThis -End $endMgLoopThis).TotalSeconds
- })
-
- $null = $script:arrayDataCollectionProgressMg.Add($mgdetail.Name)
- $progressCount = ($arrayDataCollectionProgressMg).Count
- Write-Host " $($progressCount)/$($allManagementGroupsFromEntitiesChildOfRequestedMgCount) Management Groups processed"
-
- } -ThrottleLimit $ThrottleLimit
- }
-
- $endMgLoop = Get-Date
- Write-Host " CustomDataCollection ManagementGroups processing duration: $((New-TimeSpan -Start $startMgLoop -End $endMgLoop).TotalMinutes) minutes ($((New-TimeSpan -Start $startMgLoop -End $endMgLoop).TotalSeconds) seconds)"
-
- apiCallTracking -stage 'CustomDataCollection ManagementGroups' -spacing ' '
-
- #test
- if ($builtInPolicyDefinitionsCount -ne ($($htCacheDefinitionsPolicy).Values.where({ $_.Type -eq 'BuiltIn' }).Count) -or $builtInPolicyDefinitionsCount -ne ((($htCacheDefinitionsPolicy).Values.where( { $_.Type -eq 'BuiltIn' } )).Count)) {
- Write-Host "$builtInPolicyDefinitionsCount -ne $($($htCacheDefinitionsPolicy).Values.where({$_.Type -eq 'BuiltIn'}).Count) OR $builtInPolicyDefinitionsCount -ne $((($htCacheDefinitionsPolicy).Values.where( {$_.Type -eq 'BuiltIn'} )).Count)"
- Write-Host 'Listing all PolicyDefinitions:'
- foreach ($tmpPolicyDefinitionId in ($($htCacheDefinitionsPolicy).Keys | Sort-Object)) {
- Write-Host $tmpPolicyDefinitionId
- }
- }
-
-
- #region SUBSCRIPTION
- Write-Host ' CustomDataCollection Subscriptions'
-
- if ($outOfScopeSubscriptions.Count -gt 0) {
- Write-Host " CustomDataCollection $($outOfScopeSubscriptions.Count) Subscriptions excluded" -ForegroundColor yellow
- $outOfScopeSubscriptionsGroupedByOutOfScopeReason = $outOfScopeSubscriptions | Group-Object -Property outOfScopeReason
- foreach ($exclusionreason in $outOfScopeSubscriptionsGroupedByOutOfScopeReason) {
- Write-Host " $($exclusionreason.Count): $($exclusionreason.Name)"
- }
- }
-
- Write-Host " CustomDataCollection Subscriptions will process $subsToProcessInCustomDataCollectionCount of $childrenSubscriptionsCount"
-
- $startSubLoop = Get-Date
- if ($subsToProcessInCustomDataCollectionCount -gt 0) {
-
- $counterBatch = [PSCustomObject] @{ Value = 0 }
- $batchSize = 100
- if ($subsToProcessInCustomDataCollectionCount -gt 500) {
- $batchSize = 200
- }
- Write-Host " Subscriptions Batch size: $batchSize"
-
- $subscriptionsBatch = $subsToProcessInCustomDataCollection | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
- $batchCnt = 0
- foreach ($batch in $subscriptionsBatch) {
- $startBatch = Get-Date
- $batchCnt++
- Write-Host " processing Batch #$batchCnt/$(($subscriptionsBatch | Measure-Object).Count) ($(($batch.Group | Measure-Object).Count) Subscriptions)"
- showMemoryUsage
-
- $batch.Group | ForEach-Object -Parallel {
- $startSubLoopThis = Get-Date
- $childMgSubDetail = $_
- #region UsingVARs
- #Parameters MG&Sub related
- $CsvDelimiter = $using:CsvDelimiter
- $CsvDelimiterOpposite = $using:CsvDelimiterOpposite
- #Parameters Sub related
- #fromOtherFunctions
- $azAPICallConf = $using:azAPICallConf
- $scriptPath = $using:ScriptPath
- #Array&HTs
- $newTable = $using:newTable
- $storageAccounts = $using:storageAccounts
- $resourcesAll = $using:resourcesAll
- $resourcesIdsAll = $using:resourcesIdsAll
- $resourceGroupsAll = $using:resourceGroupsAll
- $customDataCollectionDuration = $using:customDataCollectionDuration
- $htSubscriptionsMgPath = $using:htSubscriptionsMgPath
- $htManagementGroupsMgPath = $using:htManagementGroupsMgPath
- $htResourceProvidersAll = $using:htResourceProvidersAll
- $arrayFeaturesAll = $using:arrayFeaturesAll
- $htSubscriptionTagList = $using:htSubscriptionTagList
- $htResourceTypesUniqueResource = $using:htResourceTypesUniqueResource
- $htAllTagList = $using:htAllTagList
- $htSubscriptionTags = $using:htSubscriptionTags
- $htCacheDefinitionsPolicy = $using:htCacheDefinitionsPolicy
- $htCacheDefinitionsPolicySet = $using:htCacheDefinitionsPolicySet
- $htCacheDefinitionsRole = $using:htCacheDefinitionsRole
- $htCacheDefinitionsBlueprint = $using:htCacheDefinitionsBlueprint
- $htRoleDefinitionIdsUsedInPolicy = $using:htRoleDefinitionIdsUsedInPolicy
- $htCachePolicyComplianceSUB = $using:htCachePolicyComplianceSUB
- $htCachePolicyComplianceResponseTooLargeSUB = $using:htCachePolicyComplianceResponseTooLargeSUB
- $htCacheAssignmentsRole = $using:htCacheAssignmentsRole
- $htCacheAssignmentsRBACOnResourceGroupsAndResources = $using:htCacheAssignmentsRBACOnResourceGroupsAndResources
- $htCacheAssignmentsBlueprint = $using:htCacheAssignmentsBlueprint
- $htCacheAssignmentsPolicyOnResourceGroupsAndResources = $using:htCacheAssignmentsPolicyOnResourceGroupsAndResources
- $htCacheAssignmentsPolicy = $using:htCacheAssignmentsPolicy
- $htPolicyAssignmentExemptions = $using:htPolicyAssignmentExemptions
- $htResourceLocks = $using:htResourceLocks
- $LimitPOLICYPolicyDefinitionsScopedSubscription = $using:LimitPOLICYPolicyDefinitionsScopedSubscription
- $LimitPOLICYPolicySetDefinitionsScopedSubscription = $using:LimitPOLICYPolicySetDefinitionsScopedSubscription
- $LimitPOLICYPolicyAssignmentsSubscription = $using:LimitPOLICYPolicyAssignmentsSubscription
- $LimitPOLICYPolicySetAssignmentsSubscription = $using:LimitPOLICYPolicySetAssignmentsSubscription
- $childrenSubscriptionsCount = $using:childrenSubscriptionsCount
- $subsToProcessInCustomDataCollectionCount = $using:subsToProcessInCustomDataCollectionCount
- $arrayDataCollectionProgressSub = $using:arrayDataCollectionProgressSub
- $arraySubResourcesAddArrayDuration = $using:arraySubResourcesAddArrayDuration
- $htAllSubscriptionsFromAPI = $using:htAllSubscriptionsFromAPI
- $arrayEntitiesFromAPI = $using:arrayEntitiesFromAPI
- $arrayDiagnosticSettingsMgSub = $using:arrayDiagnosticSettingsMgSub
- $htMgASCSecureScore = $using:htMgASCSecureScore
- $htRoleAssignmentsFromAPIInheritancePrevention = $using:htRoleAssignmentsFromAPIInheritancePrevention
- $htNamingValidation = $using:htNamingValidation
- $htPrincipals = $using:htPrincipals
- $htServicePrincipals = $using:htServicePrincipals
- $htUserTypesGuest = $using:htUserTypesGuest
- $arrayDefenderPlans = $using:arrayDefenderPlans
- $arrayDefenderPlansSubscriptionsSkipped = $using:arrayDefenderPlansSubscriptionsSkipped
- $arrayUserAssignedIdentities4Resources = $using:arrayUserAssignedIdentities4Resources
- $htSubscriptionsRoleAssignmentLimit = $using:htSubscriptionsRoleAssignmentLimit
- $arrayPsRule = $using:arrayPsRule
- $arrayPSRuleTracking = $using:arrayPSRuleTracking
- $htClassicAdministrators = $using:htClassicAdministrators
- $htRoleAssignmentsPIM = $using:htRoleAssignmentsPIM
- $alzPolicies = $using:alzPolicies
- $alzPolicySets = $using:alzPolicySets
- $alzPolicyHashes = $using:alzPolicyHashes
- $alzPolicySetHashes = $using:alzPolicySetHashes
- $htDoARMRoleAssignmentScheduleInstances = $using:htDoARMRoleAssignmentScheduleInstances
- $htDefenderEmailContacts = $using:htDefenderEmailContacts
- $arrayVNets = $using:arrayVNets
- $arrayPrivateEndPoints = $using:arrayPrivateEndPoints
- $htResourceProvidersRef = $using:htResourceProvidersRef
- $arrayPrivateEndPointsFromResourceProperties = $using:arrayPrivateEndPointsFromResourceProperties
- $htResourcePropertiesConvertfromJSONFailed = $using:htResourcePropertiesConvertfromJSONFailed
- $htAvailablePrivateEndpointTypes = $using:htAvailablePrivateEndpointTypes
- $arrayAdvisorScores = $using:arrayAdvisorScores
- $ValidPolicyEffects = $using:ValidPolicyEffects
- #$htResourcesWithProperties = $using:htResourcesWithProperties
- #other
- $function:addRowToTable = $using:funcAddRowToTable
- $function:namingValidation = $using:funcNamingValidation
- $function:resolveObjectIds = $using:funcResolveObjectIds
- $function:testGuid = $using:funcTestGuid
- $function:dataCollectionMGSecureScore = $using:funcDataCollectionMGSecureScore
- $function:dataCollectionDefenderPlans = $using:funcDataCollectionDefenderPlans
- $function:dataCollectionDiagnosticsSub = $using:funcDataCollectionDiagnosticsSub
- $function:dataCollectionResources = $using:funcDataCollectionResources
- $function:dataCollectionStorageAccounts = $using:funcDataCollectionStorageAccounts
- $function:dataCollectionResourceGroups = $using:funcDataCollectionResourceGroups
- $function:dataCollectionResourceProviders = $using:funcDataCollectionResourceProviders
- $function:dataCollectionFeatures = $using:funcDataCollectionFeatures
- $function:dataCollectionResourceLocks = $using:funcDataCollectionResourceLocks
- $function:dataCollectionTags = $using:funcDataCollectionTags
- $function:dataCollectionPolicyComplianceStates = $using:funcDataCollectionPolicyComplianceStates
- $function:dataCollectionASCSecureScoreSub = $using:funcDataCollectionASCSecureScoreSub
- $function:dataCollectionBluePrintDefinitionsSub = $using:funcDataCollectionBluePrintDefinitionsSub
- $function:dataCollectionBluePrintAssignmentsSub = $using:funcDataCollectionBluePrintAssignmentsSub
- $function:dataCollectionPolicyExemptions = $using:funcDataCollectionPolicyExemptions
- $function:dataCollectionPolicyDefinitions = $using:funcDataCollectionPolicyDefinitions
- $function:dataCollectionPolicySetDefinitions = $using:funcDataCollectionPolicySetDefinitions
- $function:dataCollectionPolicyAssignmentsSub = $using:funcDataCollectionPolicyAssignmentsSub
- $function:dataCollectionRoleDefinitions = $using:funcDataCollectionRoleDefinitions
- $function:dataCollectionRoleAssignmentsSub = $using:funcDataCollectionRoleAssignmentsSub
- $function:dataCollectionClassicAdministratorsSub = $using:funcDataCollectionClassicAdministratorsSub
- $function:dataCollectionDefenderEmailContacts = $using:funcDataCollectionDefenderEmailContacts
- $function:dataCollectionVNets = $using:funcDataCollectionVNets
- $function:dataCollectionPrivateEndpoints = $using:funcDataCollectionPrivateEndpoints
- $function:dataCollectionAdvisorScores = $using:funcDataCollectionAdvisorScores
- $function:detectPolicyEffect = $using:funcDetectPolicyEffect
- #endregion UsingVARs
-
- $addRowToTableDone = $false
-
- $childMgSubId = $childMgSubDetail.subscriptionId
- $childMgSubDisplayName = $childMgSubDetail.subscriptionName
- $hierarchyInfo = $htSubscriptionsMgPath.($childMgSubDetail.subscriptionId)
- $hierarchyLevel = $hierarchyInfo.level
- $childMgId = $hierarchyInfo.Parent
- $childMgDisplayName = $hierarchyInfo.ParentName
- $childMgMgPath = $hierarchyInfo.pathDelimited
- $childMgParentNameChain = $hierarchyInfo.ParentNameChain
- $childMgParentNameChainDelimited = $hierarchyInfo.ParentNameChainDelimited
- $childMgParentInfo = $htManagementGroupsMgPath.($childMgId)
- $childMgParentId = $childMgParentInfo.Parent
- $childMgParentName = $htManagementGroupsMgPath.($childMgParentInfo.Parent).DisplayName
-
- #namingValidation
- if (-not [string]::IsNullOrEmpty($childMgSubDisplayName)) {
- $namingValidationResult = NamingValidation -toCheck $childMgSubDisplayName
- if ($namingValidationResult.Count -gt 0) {
-
- $script:htNamingValidation.Subscription.($childMgSubId) = @{}
- $script:htNamingValidation.Subscription.($childMgSubId).displayNameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.Subscription.($childMgSubId).displayName = $childMgSubDisplayName
- }
- }
-
- if ($azAPICallConf['htParameters'].HierarchyMapOnly -eq $false) {
- $currentSubscription = $htAllSubscriptionsFromAPI.($childMgSubId).subDetails
- $subscriptionQuotaId = $currentSubscription.subscriptionPolicies.quotaId
- $subscriptionState = $currentSubscription.state
-
- $targetMgOrSub = 'Sub'
- $baseParameters = @{
- scopeId = $childMgSubId
- scopeDisplayName = $childMgSubDisplayName
- subscriptionQuotaId = $subscriptionQuotaId
- }
-
- if (-not $azAPICallConf['htParameters'].ManagementGroupsOnly) {
- #mgSecureScore
- $mgAscSecureScoreResult = DataCollectionMGSecureScore -Id $childMgId
-
- #defenderPlans
- $dataCollectionDefenderPlansParameters = @{
- ChildMgMgPath = $childMgMgPath
- }
- DataCollectionDefenderPlans @baseParameters @dataCollectionDefenderPlansParameters
-
- #defenderEmailContacts
- DataCollectionDefenderEmailContacts @baseParameters
-
- #advisorScores
- $dataCollectionAdvisorScoresParameters = @{
- ChildMgMgPath = $childMgMgPath
- }
- DataCollectionAdvisorScores @baseParameters @dataCollectionAdvisorScoresParameters
-
- if (-not $azAPICallConf['htParameters'].NoNetwork) {
- #VNets
- DataCollectionVNets @baseParameters
- #PE
- DataCollectionPrivateEndpoints @baseParameters
- }
-
- #diagnostics
- $dataCollectionDiagnosticsSubParameters = @{
- ChildMgMgPath = $childMgMgPath
- ChildMgId = $childMgId
- }
- DataCollectionDiagnosticsSub @baseParameters @dataCollectionDiagnosticsSubParameters
-
- if ($azAPICallConf['htParameters'].NoStorageAccountAccessAnalysis -eq $false) {
- #resources
- $dataCollectionStorageAccountsParameters = @{
- ChildMgMgPath = $childMgMgPath
- ChildMgParentNameChainDelimited = $childMgParentNameChainDelimited
- }
- DataCollectionStorageAccounts @baseParameters @dataCollectionStorageAccountsParameters
- }
-
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- #resources
- $dataCollectionResourcesParameters = @{
- ChildMgMgPath = $childMgMgPath
- ChildMgParentNameChainDelimited = $childMgParentNameChainDelimited
- }
- DataCollectionResources @baseParameters @dataCollectionResourcesParameters
- }
-
- #resourceGroups
- DataCollectionResourceGroups @baseParameters
-
- #resourceProviders
- if ($azAPICallConf['htParameters'].NoResourceProvidersAtAll -eq $false) {
- DataCollectionResourceProviders @baseParameters
- }
-
- #features
- DataCollectionFeatures @baseParameters -MgParentNameChain $childMgParentNameChain
-
- #resourceLocks
- DataCollectionResourceLocks @baseParameters
-
- #tags
- $subscriptionTagsReturn = DataCollectionTags @baseParameters
- $subscriptionTags = $subscriptionTagsReturn.subscriptionTags
- $subscriptionTagsCount = $subscriptionTagsReturn.subscriptionTagsCount
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- #SubscriptionPolicyCompliance
- DataCollectionPolicyComplianceStates @baseParameters -TargetMgOrSub $targetMgOrSub
- }
-
- #SubscriptionASCSecureScore
- $subscriptionASCSecureScore = DataCollectionASCSecureScoreSub @baseParameters
-
- $addRowToTableParameters = @{
- hierarchyLevel = $hierarchyLevel
- childMgDisplayName = $childMgDisplayName
- childMgId = $childMgId
- childMgParentId = $childMgParentId
- childMgParentName = $childMgParentName
- mgAscSecureScoreResult = $mgAscSecureScoreResult
- #subscriptionQuotaId = $subscriptionQuotaId
- subscriptionState = $subscriptionState
- subscriptionASCSecureScore = $subscriptionASCSecureScore
- subscriptionTags = $subscriptionTags
- subscriptionTagsCount = $subscriptionTagsCount
- }
-
- #SubscriptionBlueprintDefinitions
- $functionReturn = DataCollectionBluePrintDefinitionsSub @baseParameters @addRowToTableParameters
- if ($functionReturn.'addRowToTableDone') {
- $addRowToTableDone = $true
- }
-
- #SubscriptionBlueprintAssignments
- $functionReturn = DataCollectionBluePrintAssignmentsSub @baseParameters @addRowToTableParameters
- if ($functionReturn.'addRowToTableDone') {
- $addRowToTableDone = $true
- }
-
- #SubscriptionPolicyExemptions
- DataCollectionPolicyExemptions @baseParameters -TargetMgOrSub $targetMgOrSub
-
- #SubscriptionPolicyDefinitions
- $functionReturn = DataCollectionPolicyDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
- $policyDefinitionsScopedCount = $functionReturn.'PolicyDefinitionsScopedCount'
-
- #SubscriptionPolicySets
- $functionReturn = DataCollectionPolicySetDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
- $policySetDefinitionsScopedCount = $functionReturn.'PolicySetDefinitionsScopedCount'
-
- $scopedPolicyCounts = @{
- policyDefinitionsScopedCount = $policyDefinitionsScopedCount
- policySetDefinitionsScopedCount = $policySetDefinitionsScopedCount
- }
-
- #SubscriptionPolicyAssignments
- $functionReturn = DataCollectionPolicyAssignmentsSub @baseParameters @addRowToTableParameters @scopedPolicyCounts
- if ($functionReturn.'addRowToTableDone') {
- $addRowToTableDone = $true
- }
-
- #SubscriptionRoleDefinitions
- DataCollectionRoleDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
-
- #SubscriptionRoleAssignments
- $functionReturn = DataCollectionRoleAssignmentsSub @baseParameters @addRowToTableParameters
- if ($functionReturn.'addRowToTableDone') {
- $addRowToTableDone = $true
- }
-
- #SubscriptionClassicAdministrators
- dataCollectionClassicAdministratorsSub @baseParameters -SubscriptionMgPath $childMgMgPath
- }
-
- if ($addRowToTableDone -ne $true) {
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $childMgDisplayName `
- -mgId $childMgId `
- -mgParentId $childMgParentId `
- -mgParentName $childMgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Subscription $childMgSubDisplayName `
- -SubscriptionId $childMgSubId `
- -SubscriptionASCSecureScore $subscriptionASCSecureScore
- }
- }
- else {
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $childMgDisplayName `
- -mgId $childMgId `
- -mgParentId $childMgParentId `
- -mgParentName $childMgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Subscription $childMgSubDisplayName `
- -SubscriptionId $childMgSubId `
- -SubscriptionASCSecureScore $subscriptionASCSecureScore
- }
- $endSubLoopThis = Get-Date
- $null = $script:customDataCollectionDuration.Add([PSCustomObject]@{
- Type = 'SUB'
- Id = $childMgSubId
- DurationSec = (New-TimeSpan -Start $startSubLoopThis -End $endSubLoopThis).TotalSeconds
- })
-
- $null = $script:arrayDataCollectionProgressSub.Add($childMgSubId)
- $progressCount = ($arrayDataCollectionProgressSub).Count
- Write-Host " $($progressCount)/$($subsToProcessInCustomDataCollectionCount) Subscriptions processed"
-
- } -ThrottleLimit $ThrottleLimit
-
- $endBatch = Get-Date
- Write-Host " Batch #$batchCnt processing duration: $((New-TimeSpan -Start $startBatch -End $endBatch).TotalMinutes) minutes ($((New-TimeSpan -Start $startBatch -End $endBatch).TotalSeconds) seconds)"
- }
-
- $endSubLoop = Get-Date
- Write-Host " CustomDataCollection Subscriptions processing duration: $((New-TimeSpan -Start $startSubLoop -End $endSubLoop).TotalMinutes) minutes ($((New-TimeSpan -Start $startSubLoop -End $endSubLoop).TotalSeconds) seconds)"
- if ($azAPICallConf['htParameters'].DoPSRule -eq $true) {
- if ($arrayPSRuleTracking.Count -gt 0) {
- $durationPSRuleTotalSeconds = (($arrayPSRuleTracking.duration | Measure-Object -Sum).Sum)
- Write-Host " CustomDataCollection Subscriptions 'PSRule for Azure' processing duration (in sum): $($durationPSRuleTotalSeconds / 60) minutes ($($durationPSRuleTotalSeconds) seconds)"
- }
- }
- #test
- Write-Host " built-in PolicyDefinitions: $($($htCacheDefinitionsPolicy).Values.where({$_.Type -eq 'BuiltIn'}).Count)"
- Write-Host " custom PolicyDefinitions: $($($htCacheDefinitionsPolicy).Values.where({$_.Type -eq 'Custom'}).Count)"
- Write-Host " all PolicyDefinitions: $($($htCacheDefinitionsPolicy).Values.Count)"
- }
- #endregion SUBSCRIPTION
-
- $durationDataMG = $customDataCollectionDuration.where( { $_.Type -eq 'MG' } )
- $durationDataSUB = $customDataCollectionDuration.where( { $_.Type -eq 'SUB' } )
- $durationMGAverageMaxMin = ($durationDataMG.DurationSec | Measure-Object -Average -Maximum -Minimum)
- $durationSUBAverageMaxMin = ($durationDataSUB.DurationSec | Measure-Object -Average -Maximum -Minimum)
- Write-Host "Collecting custom data for $($arrayEntitiesFromAPIManagementGroupsCount) ManagementGroups Avg/Max/Min duration in seconds: Average: $([math]::Round($durationMGAverageMaxMin.Average,4)); Maximum: $([math]::Round($durationMGAverageMaxMin.Maximum,4)); Minimum: $([math]::Round($durationMGAverageMaxMin.Minimum,4))"
- Write-Host "Collecting custom data for $($arrayEntitiesFromAPISubscriptionsCount) Subscriptions Avg/Max/Min duration in seconds: Average: $([math]::Round($durationSUBAverageMaxMin.Average,4)); Maximum: $([math]::Round($durationSUBAverageMaxMin.Maximum,4)); Minimum: $([math]::Round($durationSUBAverageMaxMin.Minimum,4))"
-
- apiCallTracking -stage 'CustomDataCollection ManagementGroups and Subscriptions' -spacing ' '
-
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
-
- $script:resourcesAllGroupedBySubcriptionId = $resourcesAll | Group-Object -Property subscriptionId
-
- $totaldurationSubResourcesAddArray = ($arraySubResourcesAddArrayDuration.DurationSec | Measure-Object -Sum).Sum
- Write-Host "Collecting custom data total duration writing the subResourcesArray: $totaldurationSubResourcesAddArray seconds"
-
- if (-not $azAPICallConf['htParameters'].HierarchyMapOnly -and -not $azAPICallConf['htParameters'].ManagementGroupsOnly) {
- if (-not $NoCsvExport) {
-
- #fluctuation
- Write-Host 'Process Resource fluctuation'
- $start = Get-Date
- if (Test-Path -Filter "*$($ManagementGroupId)_ResourcesAll.csv" -LiteralPath "$($outputPath)") {
- $startImportPrevious = Get-Date
- $doResourceFluctuation = $true
-
- try {
- $previous = Get-ChildItem -Path $outputPath -Filter "*$($ManagementGroupId)_ResourcesAll.csv" | Sort-Object -Descending -Property LastWriteTime | Select-Object -First 1 -ErrorAction Stop
- $importPrevious = Import-Csv -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($previous.Name)" -Encoding utf8 -Delimiter $CsvDelimiter | Select-Object -ExpandProperty id
- Write-Host " Import previous ($($previous.Name)) duration: $((New-TimeSpan -Start $startImportPrevious -End (Get-Date)).TotalSeconds) seconds"
- }
- catch {
- Write-Host " FAILED: importing previous CSV '$($outputPath)$($DirectorySeparatorChar)$($previous.Name)' OR it does not exist (*$($ManagementGroupId)_ResourcesAll.csv)"
- $doResourceFluctuation = $false
- }
-
- if ($doResourceFluctuation) {
- #$importPrevious.Count
-
- #https://gist.github.com/fatherjack/4c91cc6832b8b02d1b7319716a5fba52
- function Compare-StringSet {
- <#
- .SYNOPSIS
- Compare two sets of strings and see the matched and unmatched elements from each input
-
- .DESCRIPTION
- Compares sets of
-
- .PARAMETER Ref
- The reference set of values to be compared
-
- .PARAMETER Diff
- The difference set of values to be compared
-
- .PARAMETER CaseSensitive
- Enables a case-sensitive comparison
-
- .EXAMPLE
- $ref, $dif = @(
- , @('a', 'b', 'c')
- , @('b', 'c', 'd')
- )
- $Sets = Compare-StringSet $ref $dif
- $Sets.RefOnly
-
- $Sets.DiffOnly
-
- $Sets.Both
-
- This example sets up two arrays with some similar values and then passes them both to the Compare-StringSet function. the results of this are stored in the variable $Sets.
- $Sets is an object that has three properties - RefOnly, DiffOnly, and Both. These are sets of the incoming values where they intersect or not.
-
- .EXAMPLE
- $ref, $dif = @(
- , @('tree', 'house', 'football')
- , @('dog', 'cat', 'tree', 'house', 'Football')
- )
- $Sets = Compare-StringSet $ref $dif -CaseSensitive
- $Sets.RefOnly
- $Sets.DiffOnly
- $Sets.Both
-
- This example sets up two arrays with some similar values and then passes them both to the Compare-StringSet function using the -CaseSensitive switch. The results of this are stored in the variable $Sets.
- $Sets is an object that has three properties - RefOnly, DiffOnly, and Both.
-
- Because of the -CaseSensitive switch usage 'football' is shown as in RefOnly and 'Football' is shown as in DiffOnly.
-
- .NOTES
- From https://gist.github.com/IISResetMe/57ce7b76e1001974a4f7170e10775875
- #>
-
- param(
- [string[]]$Ref,
- [string[]]$Diff,
-
- [switch]$CaseSensitive
- )
-
- $Comparer = if ($CaseSensitive) {
- [System.StringComparer]::InvariantCulture
- }
- else {
- [System.StringComparer]::InvariantCultureIgnoreCase
- }
-
- $Results = [ordered]@{
- RefOnly = @()
- Both = @()
- DiffOnly = @()
- }
-
- $temp = [System.Collections.Generic.HashSet[string]]::new($Ref, $Comparer)
- $temp.IntersectWith($Diff)
- $Results['Both'] = $temp
-
- #$temp = [System.Collections.Generic.HashSet[string]]::new($Ref, [System.StringComparer]::CurrentCultureIgnoreCase)
- $temp = [System.Collections.Generic.HashSet[string]]::new($Ref, $Comparer)
- $temp.ExceptWith($Diff)
- $Results['RefOnly'] = $temp
-
- #$temp = [System.Collections.Generic.HashSet[string]]::new($Diff, [System.StringComparer]::CurrentCultureIgnoreCase)
- $temp = [System.Collections.Generic.HashSet[string]]::new($Diff, $Comparer)
- $temp.ExceptWith($Ref)
- $Results['DiffOnly'] = $temp
-
- return [pscustomobject]$Results
- }
-
- Write-Host " Comparing previous ($($importPrevious.Count)) with latest ($($resourcesIdsAll.Count))"
- $start = Get-Date
- $x = Compare-StringSet $importPrevious $resourcesIdsAll.id
- Write-Host ' unique values in previous (deleted):' $x.RefOnly.Count
- Write-Host " values that are contained in previous and latest: $($x.Both.Count)"
- Write-Host ' unique values in latest (added):' $x.DiffOnly.Count
- $end = Get-Date
- Write-Host " Compare previous with latest duration: $((New-TimeSpan -Start $start -End $end).TotalMinutes) mins ($((New-TimeSpan -Start $start -End $end).TotalSeconds) sec)"
-
- $script:arrayResourceFluctuationFinal = [System.Collections.ArrayList]@()
-
- #ADDED
- $arrayAdded = [System.Collections.ArrayList]@()
- $arrayAddedAndRemoved = [System.Collections.ArrayList]@()
- foreach ($resource in $x.DiffOnly) {
- $resourceSplitted = $resource.split('/')
- #$resourceSplitted
-
- $null = $arrayAdded.Add([PSCustomObject]@{
- subscriptionId = $resourceSplitted[2]
- resourceType0 = $resourceSplitted[6]
- resourceType1 = $resourceSplitted[7]
- resourceType2 = $resourceSplitted[9]
- resourceType3 = $resourceSplitted[11]
- })
-
- $subDetails = $htSubscriptionsMgPath.($resourceSplitted[2])
- $null = $arrayAddedAndRemoved.Add([pscustomobject]@{
- action = 'add'
- subscriptionId = $resourceSplitted[2]
- subscriptionName = $subDetails.displayName
- mgPath = $subDetails.pathDelimited
- resourceId = $resource
- resourceType0 = $resourceSplitted[6]
- resourceType1 = $resourceSplitted[7]
- resourceType2 = $resourceSplitted[9]
- resourceType3 = $resourceSplitted[11]
- })
-
- if ($resourceSplitted.Count -gt 13) {
- Write-Host ' Unforeseen Resource type!'
- Write-Host " Please report this Resource type at $($GithubRepository): '$resource'"
- }
- }
-
- if ($arrayAdded.Count -gt 0) {
- $arrayGroupedByResourceType = $arrayAdded | Group-Object -Property resourceType0, resourceType1, resourceType2, resourceType3
- foreach ($resourceType in $arrayGroupedByResourceType) {
- $arrayGroupedBySubscription = $arrayGroupedByResourceType.where({ $_.Name -eq $resourceType.Name }).Group | Group-Object -Property subscriptionId | Select-Object -ExcludeProperty Group
- $null = $arrayResourceFluctuationFinal.Add([PSCustomObject]@{
- Event = 'Added'
- ResourceType = ($resourceType.Name -replace ', ', '/')
- 'Resource count' = $resourceType.Count
- 'Subscription count' = ($arrayGroupedBySubscription | Measure-Object).Count
- })
- }
- }
-
- #REMOVED
- $arrayRemoved = [System.Collections.ArrayList]@()
- foreach ($resource in $x.RefOnly) {
- $resourceSplitted = $resource.split('/')
- #$resourceSplitted
-
- $null = $arrayRemoved.Add([PSCustomObject]@{
- subscriptionId = $resourceSplitted[2]
- resourceType0 = $resourceSplitted[6]
- resourceType1 = $resourceSplitted[7]
- resourceType2 = $resourceSplitted[9]
- resourceType3 = $resourceSplitted[11]
- })
-
- $subDetails = $htSubscriptionsMgPath.($resourceSplitted[2])
- $null = $arrayAddedAndRemoved.Add([pscustomobject]@{
- action = 'remove'
- subscriptionId = $resourceSplitted[2]
- subscriptionName = $subDetails.displayName
- mgPath = $subDetails.pathDelimited
- resourceId = $resource
- resourceType0 = $resourceSplitted[6]
- resourceType1 = $resourceSplitted[7]
- resourceType2 = $resourceSplitted[9]
- resourceType3 = $resourceSplitted[11]
- })
-
- if ($resourceSplitted.Count -gt 13) {
- Write-Host ' Unforeseen Resource type!'
- Write-Host " Please report this Resource type at $($GithubRepository): '$resource'"
- }
- }
-
- if ($arrayRemoved.Count -gt 0) {
- $arrayGroupedByResourceType = $arrayRemoved | Group-Object -Property resourceType0, resourceType1, resourceType2, resourceType3
- foreach ($resourceType in $arrayGroupedByResourceType) {
- $arrayGroupedBySubscription = $arrayGroupedByResourceType.where({ $_.Name -eq $resourceType.Name }).Group | Group-Object -Property subscriptionId | Select-Object -ExcludeProperty Group
- $null = $arrayResourceFluctuationFinal.Add([PSCustomObject]@{
- Event = 'Removed'
- ResourceType = ($resourceType.Name -replace ', ', '/')
- 'Resource count' = $resourceType.Count
- 'Subscription count' = ($arrayGroupedBySubscription | Measure-Object).Count
- })
- }
- }
- }
- }
- else {
- Write-Host " Process Resource fluctuation skipped, no previous output (*$($ManagementGroupId)_ResourcesAll.csv) found"
- }
-
- if ($arrayResourceFluctuationFinal.Count -gt 0 -and $doResourceFluctuation) {
- if (-not $NoCsvExport) {
- #DataCollection Export of Resource fluctuation
- Write-Host " Exporting ResourceFluctuation CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName)_ResourceFluctuation.csv'"
- $arrayResourceFluctuationFinal | Sort-Object -Property ResourceType | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_ResourceFluctuation.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
-
- Write-Host " Exporting ResourceFluctuation detailed CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName)_ResourceFluctuationDetailed.csv'"
- $arrayAddedAndRemoved | Sort-Object -Property Resource | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_ResourceFluctuationDetailed.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
- }
- Write-Host "Process Resource fluctuation duration: $((New-TimeSpan -Start $start -End (Get-Date)).TotalSeconds) seconds"
-
- #DataCollection Export of All Resources
- if ($resourcesIdsAll.Count -gt 0) {
- if (-not $NoCsvExport) {
- Write-Host "Exporting ResourcesAll CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName)_ResourcesAll.csv'"
- $resourcesIdsAll | Sort-Object -Property id | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_ResourcesAll.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
- }
- else {
- Write-Host "Not Exporting ResourcesAll CSV, as there are $($resourcesIdsAll.Count) resources"
- }
- }
- }
- }
-
- if ($azAPICallConf['htParameters'].LargeTenant -eq $false -or $azAPICallConf['htParameters'].PolicyAtScopeOnly -eq $false -or $azAPICallConf['htParameters'].RBACAtScopeOnly -eq $false) {
- if (($azAPICallConf['checkContext']).Tenant.Id -ne $ManagementGroupId) {
- addRowToTable `
- -level (($htManagementGroupsMgPath.($ManagementGroupId).ParentNameChain | Measure-Object).Count - 1) `
- -mgName $getMgParentName `
- -mgId $getMgParentId `
- -mgParentId "'upperScopes'" `
- -mgParentName 'upperScopes'
- }
- }
-
- if ($azAPICallConf['htParameters'].LargeTenant -eq $true -or $azAPICallConf['htParameters'].PolicyAtScopeOnly -eq $true) {
- if (($azAPICallConf['checkContext']).Tenant.Id -ne $ManagementGroupId) {
- $currentTask = "Policy assignments ('$($ManagementGroupId)')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementgroups/$($ManagementGroupId)/providers/Microsoft.Authorization/policyAssignments?`$filter=atScope()&api-version=2021-06-01"
- $method = 'GET'
- $upperScopesPolicyAssignments = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $upperScopesPolicyAssignments = $upperScopesPolicyAssignments | Where-Object { $_.properties.scope -ne "/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)" }
- $upperScopesPolicyAssignmentsPolicyCount = (($upperScopesPolicyAssignments | Where-Object { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' })).count
- $upperScopesPolicyAssignmentsPolicySetCount = (($upperScopesPolicyAssignments | Where-Object { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/' })).count
- $upperScopesPolicyAssignmentsPolicyAtScopeCount = (($upperScopesPolicyAssignments | Where-Object { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' -and $_.Id -match "/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)" })).count
- $upperScopesPolicyAssignmentsPolicySetAtScopeCount = (($upperScopesPolicyAssignments | Where-Object { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/' -and $_.Id -match "/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)" })).count
- $upperScopesPolicyAssignmentsPolicyAndPolicySetAtScopeCount = ($upperScopesPolicyAssignmentsPolicyAtScopeCount + $upperScopesPolicyAssignmentsPolicySetAtScopeCount)
- foreach ($L0mgmtGroupPolicyAssignment in $upperScopesPolicyAssignments) {
-
- if ($L0mgmtGroupPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' -OR $L0mgmtGroupPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/') {
- if ($L0mgmtGroupPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/') {
- $PolicyVariant = 'Policy'
- $Id = ($L0mgmtGroupPolicyAssignment.properties.policydefinitionid).ToLower()
- $Def = ($htCacheDefinitionsPolicy).($Id)
- $PolicyAssignmentScope = $L0mgmtGroupPolicyAssignment.Properties.Scope
- #$PolicyAssignmentNotScopes = $L0mgmtGroupPolicyAssignment.Properties.NotScopes -join "$CsvDelimiterOpposite "
- $PolicyAssignmentId = ($L0mgmtGroupPolicyAssignment.Id).ToLower()
- $PolicyAssignmentName = $L0mgmtGroupPolicyAssignment.Name
- $PolicyAssignmentDisplayName = $L0mgmtGroupPolicyAssignment.Properties.DisplayName
- if (($L0mgmtGroupPolicyAssignment.Properties.Description).length -eq 0) {
- $PolicyAssignmentDescription = 'no description given'
- }
- else {
- $PolicyAssignmentDescription = $L0mgmtGroupPolicyAssignment.Properties.Description
- }
-
- if ($L0mgmtGroupPolicyAssignment.identity) {
- $PolicyAssignmentIdentity = $L0mgmtGroupPolicyAssignment.identity.principalId
- }
- else {
- $PolicyAssignmentIdentity = 'n/a'
- }
-
- if ($Def.Type -eq 'Custom') {
- $policyDefintionScope = $Def.Scope
- $policyDefintionScopeMgSub = $Def.ScopeMgSub
- $policyDefintionScopeId = $Def.ScopeId
- }
- else {
- $policyDefintionScope = 'n/a'
- $policyDefintionScopeMgSub = 'n/a'
- $policyDefintionScopeId = 'n/a'
- }
-
- $assignedBy = 'n/a'
- $createdBy = ''
- $createdOn = ''
- $updatedBy = ''
- $updatedOn = ''
- if ($L0mgmtGroupPolicyAssignment.properties.metadata) {
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.assignedBy) {
- $assignedBy = $L0mgmtGroupPolicyAssignment.properties.metadata.assignedBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.createdBy) {
- $createdBy = $L0mgmtGroupPolicyAssignment.properties.metadata.createdBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.createdOn) {
- $createdOn = $L0mgmtGroupPolicyAssignment.properties.metadata.createdOn
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.updatedBy) {
- $updatedBy = $L0mgmtGroupPolicyAssignment.properties.metadata.updatedBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.updatedOn) {
- $updatedOn = $L0mgmtGroupPolicyAssignment.properties.metadata.updatedOn
- }
- }
-
- if (($L0mgmtGroupPolicyAssignment.Properties.nonComplianceMessages.where( { -not $_.policyDefinitionReferenceId })).Message) {
- $nonComplianceMessage = ($L0mgmtGroupPolicyAssignment.Properties.nonComplianceMessages.where( { -not $_.policyDefinitionReferenceId })).Message
- }
- else {
- $nonComplianceMessage = ''
- }
-
- $formatedPolicyAssignmentParameters = ''
- $hlp = $L0mgmtGroupPolicyAssignment.Properties.Parameters
- if (-not [string]::IsNullOrEmpty($hlp)) {
- $arrayPolicyAssignmentParameters = @()
- $arrayPolicyAssignmentParameters = foreach ($parameterName in $hlp.PSObject.Properties.Name | Sort-Object) {
- "$($parameterName)=$($hlp.($parameterName).Value -join "$($CsvDelimiter) ")"
- }
- $formatedPolicyAssignmentParameters = $arrayPolicyAssignmentParameters -join "$($CsvDelimiterOpposite) "
- }
-
- #mgSecureScore
- $mgAscSecureScoreResult = ''
-
- addRowToTable `
- -level (($htManagementGroupsMgPath.($ManagementGroupId).ParentNameChain).Count - 1) `
- -mgName $getMgParentName `
- -mgId $getMgParentId `
- -mgParentId "'upperScopes'" `
- -mgParentName 'upperScopes' `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Policy $Def.DisplayName `
- -PolicyDescription $Def.Description `
- -PolicyVariant $PolicyVariant `
- -PolicyType $Def.Type `
- -PolicyCategory $Def.Category `
- -PolicyDefinitionIdGuid (($Def.Id) -replace '.*/') `
- -PolicyDefinitionId $Def.PolicyDefinitionId `
- -PolicyDefintionScope $policyDefintionScope `
- -PolicyDefintionScopeMgSub $policyDefintionScopeMgSub `
- -PolicyDefintionScopeId $policyDefintionScopeId `
- -PolicyDefinitionsScopedLimit $LimitPOLICYPolicyDefinitionsScopedManagementGroup `
- -PolicyDefinitionsScopedCount $PolicyDefinitionsScopedCount `
- -PolicySetDefinitionsScopedLimit $LimitPOLICYPolicySetDefinitionsScopedManagementGroup `
- -PolicySetDefinitionsScopedCount $PolicySetDefinitionsScopedCount `
- -PolicyDefinitionEffectDefault ($htCacheDefinitionsPolicy).(($Def.PolicyDefinitionId)).effectDefaultValue `
- -PolicyDefinitionEffectFixed ($htCacheDefinitionsPolicy).(($Def.PolicyDefinitionId)).effectFixedValue `
- -PolicyAssignmentScope $PolicyAssignmentScope `
- -PolicyAssignmentScopeMgSubRg 'Mg' `
- -PolicyAssignmentScopeName ($PolicyAssignmentScope -replace '.*/', '') `
- -PolicyAssignmentNotScopes $L0mgmtGroupPolicyAssignment.Properties.NotScopes `
- -PolicyAssignmentId $PolicyAssignmentId `
- -PolicyAssignmentName $PolicyAssignmentName `
- -PolicyAssignmentDisplayName $PolicyAssignmentDisplayName `
- -PolicyAssignmentDescription $PolicyAssignmentDescription `
- -PolicyAssignmentEnforcementMode $L0mgmtGroupPolicyAssignment.Properties.EnforcementMode `
- -PolicyAssignmentNonComplianceMessages $L0mgmtGroupPolicyAssignment.Properties.nonComplianceMessages `
- -PolicyAssignmentIdentity $PolicyAssignmentIdentity `
- -PolicyAssignmentLimit $LimitPOLICYPolicyAssignmentsManagementGroup `
- -PolicyAssignmentCount $upperScopesPolicyAssignmentsPolicyCount `
- -PolicyAssignmentAtScopeCount $upperScopesPolicyAssignmentsPolicyAtScopeCount `
- -PolicyAssignmentParameters $L0mgmtGroupPolicyAssignment.Properties.Parameters `
- -PolicyAssignmentParametersFormated $formatedPolicyAssignmentParameters `
- -PolicyAssignmentAssignedBy $assignedBy `
- -PolicyAssignmentCreatedBy $createdBy `
- -PolicyAssignmentCreatedOn $createdOn `
- -PolicyAssignmentUpdatedBy $updatedBy `
- -PolicyAssignmentUpdatedOn $updatedOn `
- -PolicySetAssignmentLimit $LimitPOLICYPolicySetAssignmentsManagementGroup `
- -PolicySetAssignmentCount $upperScopesPolicyAssignmentsPolicySetCount `
- -PolicySetAssignmentAtScopeCount $upperScopesPolicyAssignmentsPolicySetAtScopeCount `
- -PolicyAndPolicySetAssignmentAtScopeCount $upperScopesPolicyAssignmentsPolicyAndPolicySetAtScopeCount
- }
-
- if ($L0mgmtGroupPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/') {
- $PolicyVariant = 'PolicySet'
- $Id = ($L0mgmtGroupPolicyAssignment.properties.policydefinitionid).ToLower()
- $Def = ($htCacheDefinitionsPolicySet).($Id)
- $PolicyAssignmentScope = $L0mgmtGroupPolicyAssignment.Properties.Scope
- #$PolicyAssignmentNotScopes = $L0mgmtGroupPolicyAssignment.Properties.NotScopes -join "$CsvDelimiterOpposite "
- $PolicyAssignmentId = ($L0mgmtGroupPolicyAssignment.Id).ToLower()
- $PolicyAssignmentName = $L0mgmtGroupPolicyAssignment.Name
- $PolicyAssignmentDisplayName = $L0mgmtGroupPolicyAssignment.Properties.DisplayName
- if (($L0mgmtGroupPolicyAssignment.Properties.Description).length -eq 0) {
- $PolicyAssignmentDescription = 'no description given'
- }
- else {
- $PolicyAssignmentDescription = $L0mgmtGroupPolicyAssignment.Properties.Description
- }
-
- if ($L0mgmtGroupPolicyAssignment.identity) {
- $PolicyAssignmentIdentity = $L0mgmtGroupPolicyAssignment.identity.principalId
- }
- else {
- $PolicyAssignmentIdentity = 'n/a'
- }
-
- if ($Def.Type -eq 'Custom') {
- $policyDefintionScope = $Def.Scope
- $policyDefintionScopeMgSub = $Def.ScopeMgSub
- $policyDefintionScopeId = $Def.ScopeId
- }
- else {
- $policyDefintionScope = 'n/a'
- $policyDefintionScopeMgSub = 'n/a'
- $policyDefintionScopeId = 'n/a'
- }
-
- $assignedBy = 'n/a'
- $createdBy = ''
- $createdOn = ''
- $updatedBy = ''
- $updatedOn = ''
- if ($L0mgmtGroupPolicyAssignment.properties.metadata) {
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.assignedBy) {
- $assignedBy = $L0mgmtGroupPolicyAssignment.properties.metadata.assignedBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.createdBy) {
- $createdBy = $L0mgmtGroupPolicyAssignment.properties.metadata.createdBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.createdOn) {
- $createdOn = $L0mgmtGroupPolicyAssignment.properties.metadata.createdOn
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.updatedBy) {
- $updatedBy = $L0mgmtGroupPolicyAssignment.properties.metadata.updatedBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.updatedOn) {
- $updatedOn = $L0mgmtGroupPolicyAssignment.properties.metadata.updatedOn
- }
- }
-
- if (($L0mgmtGroupPolicyAssignment.Properties.nonComplianceMessages.where( { -not $_.policyDefinitionReferenceId })).Message) {
- $nonComplianceMessage = ($L0mgmtGroupPolicyAssignment.Properties.nonComplianceMessages.where( { -not $_.policyDefinitionReferenceId })).Message
- }
- else {
- $nonComplianceMessage = ''
- }
-
- $formatedPolicyAssignmentParameters = ''
- $hlp = $L0mgmtGroupPolicyAssignment.Properties.Parameters
- if (-not [string]::IsNullOrEmpty($hlp)) {
- $arrayPolicyAssignmentParameters = @()
- $arrayPolicyAssignmentParameters = foreach ($parameterName in $hlp.PSObject.Properties.Name | Sort-Object) {
- "$($parameterName)=$($hlp.($parameterName).Value -join "$($CsvDelimiter) ")"
- }
- $formatedPolicyAssignmentParameters = $arrayPolicyAssignmentParameters -join "$($CsvDelimiterOpposite) "
- }
-
- addRowToTable `
- -level (($htManagementGroupsMgPath.($ManagementGroupId).ParentNameChain).Count - 1) `
- -mgName $getMgParentName `
- -mgId $getMgParentId `
- -mgParentId "'upperScopes'" `
- -mgParentName 'upperScopes' `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Policy $Def.DisplayName `
- -PolicyDescription $Def.Description `
- -PolicyVariant $PolicyVariant `
- -PolicyType $Def.Type `
- -PolicyCategory $Def.Category `
- -PolicyDefinitionIdGuid (($Def.Id) -replace '.*/') `
- -PolicyDefinitionId $Def.PolicyDefinitionId `
- -PolicyDefintionScope $policyDefintionScope `
- -PolicyDefintionScopeMgSub $policyDefintionScopeMgSub `
- -PolicyDefintionScopeId $policyDefintionScopeId `
- -PolicyDefinitionsScopedLimit $LimitPOLICYPolicyDefinitionsScopedManagementGroup `
- -PolicyDefinitionsScopedCount $PolicyDefinitionsScopedCount `
- -PolicySetDefinitionsScopedLimit $LimitPOLICYPolicySetDefinitionsScopedManagementGroup `
- -PolicySetDefinitionsScopedCount $PolicySetDefinitionsScopedCount `
- -PolicyAssignmentScope $PolicyAssignmentScope `
- -PolicyAssignmentScopeMgSubRg 'Mg' `
- -PolicyAssignmentScopeName ($PolicyAssignmentScope -replace '.*/', '') `
- -PolicyAssignmentNotScopes $L0mgmtGroupPolicyAssignment.Properties.NotScopes `
- -PolicyAssignmentId $PolicyAssignmentId `
- -PolicyAssignmentName $PolicyAssignmentName `
- -PolicyAssignmentDisplayName $PolicyAssignmentDisplayName `
- -PolicyAssignmentDescription $PolicyAssignmentDescription `
- -PolicyAssignmentEnforcementMode $L0mgmtGroupPolicyAssignment.Properties.EnforcementMode `
- -PolicyAssignmentNonComplianceMessages $L0mgmtGroupPolicyAssignment.Properties.nonComplianceMessages `
- -PolicyAssignmentIdentity $PolicyAssignmentIdentity `
- -PolicyAssignmentLimit $LimitPOLICYPolicyAssignmentsManagementGroup `
- -PolicyAssignmentCount $upperScopesPolicyAssignmentsPolicyCount `
- -PolicyAssignmentAtScopeCount $upperScopesPolicyAssignmentsPolicyAtScopeCount `
- -PolicyAssignmentParameters $L0mgmtGroupPolicyAssignment.Properties.Parameters `
- -PolicyAssignmentParametersFormated $formatedPolicyAssignmentParameters `
- -PolicyAssignmentAssignedBy $assignedBy `
- -PolicyAssignmentCreatedBy $createdBy `
- -PolicyAssignmentCreatedOn $createdOn `
- -PolicyAssignmentUpdatedBy $updatedBy `
- -PolicyAssignmentUpdatedOn $updatedOn `
- -PolicySetAssignmentLimit $LimitPOLICYPolicySetAssignmentsManagementGroup `
- -PolicySetAssignmentCount $upperScopesPolicyAssignmentsPolicySetCount `
- -PolicySetAssignmentAtScopeCount $upperScopesPolicyAssignmentsPolicySetAtScopeCount `
- -PolicyAndPolicySetAssignmentAtScopeCount $upperScopesPolicyAssignmentsPolicyAndPolicySetAtScopeCount
- }
- }
- }
- }
- }
-
- if ($azAPICallConf['htParameters'].LargeTenant -eq $true -or $azAPICallConf['htParameters'].RBACAtScopeOnly -eq $true) {
-
- #RoleAssignment API (system metadata e.g. createdOn)
- $currentTask = "Role assignments API '$($ManagementGroupId)'"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)/providers/Microsoft.Authorization/roleAssignments?api-version=2015-07-01"
- $method = 'GET'
- $roleAssignmentsFromAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- if ($roleAssignmentsFromAPI.Count -gt 0) {
- $principalsToResolve = @()
- $principalsToResolve = foreach ($ra in $roleAssignmentsFromAPI.properties | Sort-Object -Property principalId -Unique) {
- if (-not $htPrincipals.($ra.principalId)) {
- $ra.principalId
- }
- }
-
- if ($principalsToResolve.Count -gt 0) {
- ResolveObjectIds -objectIds $principalsToResolve
- }
- }
-
- #$upperScopesRoleAssignments = GetRoleAssignments -Scope "/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)" -scopeDetails "getRoleAssignments upperScopes (Mg)"
- $upperScopesRoleAssignments = $roleAssignmentsFromAPI
-
- $upperScopesRoleAssignmentsLimitUtilization = (($upperScopesRoleAssignments | Where-Object { $_.properties.scope -eq "/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)" })).count
- #tenantLevelRoleAssignments
- if (-not $htMgAtScopeRoleAssignments.'tenantLevelRoleAssignments') {
- $tenantLevelRoleAssignmentsCount = (($upperScopesRoleAssignments | Where-Object { $_.id -like '/providers/Microsoft.Authorization/roleAssignments/*' })).count
- $htMgAtScopeRoleAssignments.'tenantLevelRoleAssignments' = @{}
- $htMgAtScopeRoleAssignments.'tenantLevelRoleAssignments'.AssignmentsCount = $tenantLevelRoleAssignmentsCount
- }
-
- foreach ($upperScopesRoleAssignment in $upperScopesRoleAssignments) {
-
- $roleAssignmentId = ($upperScopesRoleAssignment.id).ToLower()
-
- if ($upperScopesRoleAssignment.properties.scope -ne "/providers/Microsoft.Management/managementGroups/$ManagementGroupId") {
- $roleDefinitionId = $upperScopesRoleAssignment.properties.roleDefinitionId
- $roleDefinitionIdGuid = $roleDefinitionId -replace '.*/'
-
- if (-not ($htCacheDefinitionsRole).($roleDefinitionIdGuid)) {
- $roleDefinitionName = "'This roleDefinition likely was deleted although a roleAssignment existed'"
- }
- else {
- $roleDefinitionName = ($htCacheDefinitionsRole).($roleDefinitionIdGuid).Name
- }
-
- if (($htPrincipals.($upperScopesRoleAssignment.properties.principalId).displayName).length -eq 0) {
- $roleAssignmentIdentityDisplayname = 'n/a'
- }
- else {
- if ($htPrincipals.($upperScopesRoleAssignment.properties.principalId).type -eq 'User') {
- if ($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData -eq $false) {
- $roleAssignmentIdentityDisplayname = $htPrincipals.($upperScopesRoleAssignment.properties.principalId).displayName
- }
- else {
- $roleAssignmentIdentityDisplayname = 'scrubbed'
- }
- }
- else {
- $roleAssignmentIdentityDisplayname = $htPrincipals.($upperScopesRoleAssignment.properties.principalId).displayName
- }
- }
- if (-not $htPrincipals.($upperScopesRoleAssignment.properties.principalId).signInName) {
- $roleAssignmentIdentitySignInName = 'n/a'
- }
- else {
- if ($htPrincipals.($upperScopesRoleAssignment.properties.principalId).type -eq 'User') {
- if ($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData -eq $false) {
- $roleAssignmentIdentitySignInName = $htPrincipals.($upperScopesRoleAssignment.properties.principalId).signInName
- }
- else {
- $roleAssignmentIdentitySignInName = 'scrubbed'
- }
- }
- else {
- $roleAssignmentIdentitySignInName = $htPrincipals.($upperScopesRoleAssignment.properties.principalId).signInName
- }
- }
- $roleAssignmentIdentityObjectId = $upperScopesRoleAssignment.properties.principalId
- $roleAssignmentIdentityObjectType = $htPrincipals.($upperScopesRoleAssignment.properties.principalId).type
-
- $roleAssignmentScope = $upperScopesRoleAssignment.properties.scope
- $roleAssignmentScopeName = $roleAssignmentScope -replace '.*/'
- $roleAssignmentScopeType = 'MG'
-
- $roleSecurityCustomRoleOwner = 0
- if (($htCacheDefinitionsRole).$($roleDefinitionIdGuid).Actions -eq '*' -and ((($htCacheDefinitionsRole).$($roleDefinitionIdGuid).NotActions)).length -eq 0 -and ($htCacheDefinitionsRole).$($roleDefinitionIdGuid).IsCustom -eq $True) {
- $roleSecurityCustomRoleOwner = 1
- }
- $roleSecurityOwnerAssignmentSP = 0
- if ((($htCacheDefinitionsRole).$($roleDefinitionIdGuid).Id -eq '8e3af657-a8ff-443c-a75c-2fe8c4bcb635' -and $roleAssignmentIdentityObjectType -eq 'ServicePrincipal') -or (($htCacheDefinitionsRole).$($roleDefinitionIdGuid).Actions -eq '*' -and ((($htCacheDefinitionsRole).$($roleDefinitionIdGuid).NotActions)).length -eq 0 -and ($htCacheDefinitionsRole).$($roleDefinitionIdGuid).IsCustom -eq $True -and $roleAssignmentIdentityObjectType -eq 'ServicePrincipal')) {
- $roleSecurityOwnerAssignmentSP = 1
- }
-
- $createdBy = ''
- $createdOn = ''
- $createdOnUnformatted = $null
- $updatedBy = ''
- $updatedOn = ''
-
- if ($upperScopesRoleAssignment.properties.createdBy) {
- $createdBy = $upperScopesRoleAssignment.properties.createdBy
- }
- if ($upperScopesRoleAssignment.properties.createdOn) {
- $createdOn = $upperScopesRoleAssignment.properties.createdOn
- }
- if ($upperScopesRoleAssignment.properties.updatedBy) {
- $updatedBy = $upperScopesRoleAssignment.properties.updatedBy
- }
- if ($upperScopesRoleAssignment.properties.updatedOn) {
- $updatedOn = $upperScopesRoleAssignment.properties.updatedOn
- }
- $createdOnUnformatted = $upperScopesRoleAssignment.properties.createdOn
-
- if (($azAPICallConf['checkContext']).Tenant.Id -ne $ManagementGroupId) {
- $levelToUse = (($htManagementGroupsMgPath.($ManagementGroupId).ParentNameChain).Count - 1)
- $toUseAsmgName = $getMgParentName
- $toUseAsmgId = $getMgParentId
- $toUseAsmgParentId = "'upperScopes'"
- $toUseAsmgParentName = 'upperScopes'
- }
- else {
- $levelToUse = (($htManagementGroupsMgPath.($ManagementGroupId).ParentNameChain).Count)
- $toUseAsmgName = $selectedManagementGroupId.DisplayName
- $toUseAsmgId = $selectedManagementGroupId.Name
- $toUseAsmgParentId = 'Tenant'
- $toUseAsmgParentName = 'Tenant'
- }
-
- #mgSecureScore
- $mgAscSecureScoreResult = ''
-
- addRowToTable `
- -level $levelToUse `
- -mgName $toUseAsmgName `
- -mgId $toUseAsmgId `
- -mgParentId $toUseAsmgParentId `
- -mgParentName $toUseAsmgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -RoleDefinitionId $roleDefinitionIdGuid `
- -RoleDefinitionName $roleDefinitionName `
- -RoleIsCustom ($htCacheDefinitionsRole).$($roleDefinitionIdGuid).IsCustom `
- -RoleAssignableScopes (($htCacheDefinitionsRole).$($roleDefinitionIdGuid).AssignableScopes -join "$CsvDelimiterOpposite ") `
- -RoleActions (($htCacheDefinitionsRole).$($roleDefinitionIdGuid).Actions -join "$CsvDelimiterOpposite ") `
- -RoleNotActions (($htCacheDefinitionsRole).$($roleDefinitionIdGuid).NotActions -join "$CsvDelimiterOpposite ") `
- -RoleDataActions (($htCacheDefinitionsRole).$($roleDefinitionIdGuid).DataActions -join "$CsvDelimiterOpposite ") `
- -RoleNotDataActions (($htCacheDefinitionsRole).$($roleDefinitionIdGuid).NotDataActions -join "$CsvDelimiterOpposite ") `
- -RoleAssignmentIdentityDisplayname $roleAssignmentIdentityDisplayname `
- -RoleAssignmentIdentitySignInName $roleAssignmentIdentitySignInName `
- -RoleAssignmentIdentityObjectId $roleAssignmentIdentityObjectId `
- -RoleAssignmentIdentityObjectType $roleAssignmentIdentityObjectType `
- -RoleAssignmentId $roleAssignmentId `
- -RoleAssignmentScope $roleAssignmentScope `
- -RoleAssignmentScopeName $roleAssignmentScopeName `
- -RoleAssignmentScopeType $roleAssignmentScopeType `
- -RoleAssignmentCreatedBy $createdBy `
- -RoleAssignmentCreatedOn $createdOn `
- -RoleAssignmentCreatedOnUnformatted $createdOnUnformatted `
- -RoleAssignmentUpdatedBy $updatedBy `
- -RoleAssignmentUpdatedOn $updatedOn `
- -RoleAssignmentsLimit $LimitRBACRoleAssignmentsManagementGroup `
- -RoleAssignmentsCount $upperScopesRoleAssignmentsLimitUtilization `
- -RoleSecurityCustomRoleOwner $roleSecurityCustomRoleOwner `
- -RoleSecurityOwnerAssignmentSP $roleSecurityOwnerAssignmentSP `
- -RoleAssignmentPIM 'unknown'
- }
- }
- }
-}
-function processDefinitionInsights() {
- $startDefinitionInsights = Get-Date
- Write-Host ' Building DefinitionInsights'
-
- $md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
- $utf8 = New-Object -TypeName System.Text.UTF8Encoding
-
- #region definitionInsightsAzurePolicy
- $htmlDefinitionInsights = [System.Text.StringBuilder]::new()
- [void]$htmlDefinitionInsights.AppendLine( @'
-
-
-'@)
-
- #policy/policySet preQuery
- #region preQuery
- $htPolicyWithAssignments = @{}
- $htPolicyWithAssignments.policy = @{}
- $htPolicyWithAssignments.policySet = @{}
-
- foreach ($policyOrPolicySet in $arrayPolicyAssignmentsEnriched | Sort-Object -Property PolicyAssignmentId -Unique | Group-Object -Property PolicyId, PolicyVariant) {
- $policyOrPolicySetNameSplit = $policyOrPolicySet.name.split(', ')
- if ($policyOrPolicySetNameSplit[1] -eq 'Policy') {
- #policy
- if (-not ($htPolicyWithAssignments).policy.($policyOrPolicySetNameSplit[0])) {
- $pscustomObj = [System.Collections.ArrayList]@()
- foreach ($entry in $policyOrPolicySet.group) {
- $null = $pscustomObj.Add([PSCustomObject]@{
- PolicyAssignmentId = $entry.PolicyAssignmentId
- PolicyAssignmentDisplayName = $entry.PolicyAssignmentDisplayName
- })
- }
- ($htPolicyWithAssignments).policy.($policyOrPolicySetNameSplit[0]) = @{}
- ($htPolicyWithAssignments).policy.($policyOrPolicySetNameSplit[0]).Assignments = [array]($pscustomObj)
- }
- }
- else {
- #policySet
- if (-not ($htPolicyWithAssignments).policySet.($policyOrPolicySetNameSplit[0])) {
- $pscustomObj = [System.Collections.ArrayList]@()
- foreach ($entry in $policyOrPolicySet.group) {
- $null = $pscustomObj.Add([PSCustomObject]@{
- PolicyAssignmentId = $entry.PolicyAssignmentId
- PolicyAssignmentDisplayName = $entry.PolicyAssignmentDisplayName
- })
- }
- ($htPolicyWithAssignments).policySet.($policyOrPolicySetNameSplit[0]) = @{}
- ($htPolicyWithAssignments).policySet.($policyOrPolicySetNameSplit[0]).Assignments = [array]($pscustomObj)
- }
- }
- }
-
- foreach ($customPolicy in $tenantCustomPolicies) {
- if ($htPoliciesWithAssignmentOnRgRes.($customPolicy.PolicyDefinitionId)) {
- if (-not ($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId)) {
- ($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId) = @{}
- ($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId).Assignments = [array]($htPoliciesWithAssignmentOnRgRes.($customPolicy.PolicyDefinitionId).Assignments)
- }
- else {
- $array = @()
- $array += ($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId).Assignments
- $array += $htPoliciesWithAssignmentOnRgRes.($customPolicy.PolicyDefinitionId).Assignments
- ($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId).Assignments = $array
- }
- }
- }
-
- foreach ($customPolicySet in $tenantCustomPolicySets) {
- if ($htPoliciesWithAssignmentOnRgRes.($customPolicySet.PolicyDefinitionId)) {
- if (-not ($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId)) {
- ($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId) = @{}
- ($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId).Assignments = [array]($htPoliciesWithAssignmentOnRgRes.($customPolicySet.PolicyDefinitionId).Assignments)
- }
- else {
- $array = @()
- $array += ($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId).Assignments
- $array += $htPoliciesWithAssignmentOnRgRes.($customPolicySet.PolicyDefinitionId).Assignments
- ($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId).Assignments = $array
- }
- }
- }
- #endregion preQuery
-
- #region definitionInsightsPolicyDefinitions
- $startDefinitionInsightsPolicyDefinitions = Get-Date
- Write-Host ' processing DefinitionInsights Policy definitions'
- ShowMemoryUsage
- $tfCount = $tenantAllPoliciesCount
- $htmlTableId = 'definitionInsights_Policy'
- [void]$htmlDefinitionInsights.AppendLine( @"
-
$tenantAllPoliciesCount Policy definitions
-
-
-
-
-
- Search JSON
-
-
-
-
- Builtin/Custom/Static
-
-
-
-
- ALZ
-
-
-
-
- Category
-
-
-
-
- Deprecated
-
-
-
-
- Preview
-
-
-
-
- Scope Mg/Sub
-
-
-
-
- Scope Name/Id
-
-
-
-
- Effect default
-
-
-
-
- hasAssignment
-
-
-
-
- polPolAssignments
-
-
-
-
- polhid1
-
-
-
-
- usedInPolicySet
-
-
-
-
- polUsedInPolicySetCount
-
-
-
-
- polUsedInPolicySets
-
-
-
-
- Roles
-
-
-
-
-
-
-
-
-
-
-
-JSON
-PolicyType
-ALZ
-Category
-Deprecated
-Preview
-Scope Mg/Sub
-Scope Name/Id
-effectDefaultValue
-hasAssignments
-Assignments Count
-Assignments
-UsedInPolicySet
-PolicySetsCount
-PolicySets
-Roles
-
-
-
-"@)
-
- $cnter = 0
- $htmlDefinitionInsightshlp = $null
- $htmlDefinitionInsightshlp = foreach ($policy in (($htCacheDefinitionsPolicy).Values | Sort-Object @{Expression = { $_.DisplayName } }, @{Expression = { $_.PolicyDefinitionId } })) {
-
- $cnter++
- if ($cnter % 1000 -eq 0) {
- Write-Host " $cnter Policy definitions processed"
- ShowMemoryUsage
- }
-
- $hasAssignments = 'false'
- $assignmentsCount = 0
- $assignmentsDetailed = 'n/a'
-
- if (($htPolicyWithAssignments).policy.($policy.PolicyDefinitionId)) {
- $hasAssignments = 'true'
- $assignments = ($htPolicyWithAssignments).policy.($policy.PolicyDefinitionId).Assignments
- $assignmentsCount = $assignments.Count
-
- if ($assignmentsCount -gt 0) {
- $arrayAssignmentDetails = @()
- $arrayAssignmentDetails = foreach ($assignment in $assignments) {
- if ($assignment.PolicyAssignmentDisplayName -eq '') {
- $polAssDisplayName = '#no AssignmentName given '
- }
- else {
- $polAssDisplayName = $assignment.PolicyAssignmentDisplayName
- }
- "$($assignment.PolicyAssignmentId) ($($polAssDisplayName) )"
- }
- $assignmentsDetailed = $arrayAssignmentDetails -join "$CsvDelimiterOpposite "
- }
-
- }
-
- $roleDefinitionIds = 'n/a'
- if ($policy.RoleDefinitionIds -ne 'n/a') {
- $arrayRoleDefDetails = @()
- $arrayRoleDefDetails = foreach ($roleDef in $policy.RoleDefinitionIds) {
- $roleDefIdOnly = $roleDef -replace '.*/'
- if (($roleDefIdOnly).Length -ne 36) {
- "'INVALID RoleDefId!' ($($roleDefIdOnly))"
- }
- else {
- $roleDefHlp = ($htCacheDefinitionsRole).($roleDefIdOnly)
- "'$($roleDefHlp.Name)' ($($roleDefHlp.Id))"
- }
- }
- $roleDefinitionIds = $arrayRoleDefDetails -join "$CsvDelimiterOpposite "
- }
-
- $scopeDetails = 'n/a'
- if ($policy.ScopeId -ne 'n/a') {
- if ([string]::IsNullOrEmpty($policy.ScopeId)) {
- Write-Host "unexpected IsNullOrEmpty - processing: $($policy | ConvertTo-Json -Depth 99)"
- }
- $scopeDetails = "$($policy.ScopeId) ($($htEntities.($policy.ScopeId).DisplayName))"
- }
-
- $usedInPolicySet = 'false'
- $usedInPolicySetCount = 0
- $usedInPolicySets = 'n/a'
-
- if ($htPoliciesUsedInPolicySets.($policy.PolicyDefinitionId)) {
- $usedInPolicySet = 'true'
- $usedInPolicySetCount = ($htPoliciesUsedInPolicySets.($policy.PolicyDefinitionId).policySet).Count
- $usedInPolicySets = ($htPoliciesUsedInPolicySets.($policy.PolicyDefinitionId).policySet | Sort-Object) -join "$CsvDelimiterOpposite "
- }
-
- $json = $($policy.Json | ConvertTo-Json -Depth 99)
- $guid = ([System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($policy.PolicyDefinitionId)))) -replace '-'
- @"
-
-
-
-
- Copy definition
-
-
-
-
-
-
-
-
-$($policy.Type)
-$($policy.ALZ)
-$($policy.Category -replace '<', '<' -replace '>', '>')
-$($policy.Deprecated)
-$($policy.Preview)
-$($policy.ScopeMgSub)
-$($scopeDetails -replace '<', '<' -replace '>', '>')
-$($policy.effectDefaultValue)
-$hasAssignments
-$assignmentsCount
-$assignmentsDetailed
-$usedInPolicySet
-$usedInPolicySetCount
-$usedInPolicySets
-$($roleDefinitionIds -replace '<', '<' -replace '>', '>')
-
-"@
- }
- [void]$htmlDefinitionInsights.AppendLine($htmlDefinitionInsightshlp)
- if ($NoDefinitionInsightsDedicatedHTML) {
- $htmlDefinitionInsights | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $htmlDefinitionInsights = [System.Text.StringBuilder]::new()
- }
- [void]$htmlDefinitionInsights.AppendLine( @"
-
-
-
-
-
-"@)
- $endDefinitionInsightsPolicyDefinitions = Get-Date
- Write-Host " DefinitionInsights Policy definitions duration: $((New-TimeSpan -Start $startDefinitionInsightsPolicyDefinitions -End $endDefinitionInsightsPolicyDefinitions).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefinitionInsightsPolicyDefinitions -End $endDefinitionInsightsPolicyDefinitions).TotalSeconds) seconds)"
- showMemoryUsage
- #endregion definitionInsightsPolicyDefinitions
-
- #region definitionInsightsPolicySetDefinitions
- $startDefinitionInsightsPolicySetDefinitions = Get-Date
- Write-Host ' processing DefinitionInsights PolicySet definitions'
- ShowMemoryUsage
- $tfCount = $tenantAllPolicySetsCount
- $htmlTableId = 'definitionInsights_PolicySet'
- [void]$htmlDefinitionInsights.AppendLine( @"
-
$tenantAllPolicySetsCount PolicySet definitions
-
-
-
-
-
- Search JSON
-
-
-
-
- Builtin/Custom
-
-
-
-
- ALZ
-
-
-
-
- Category
-
-
-
-
- Deprecated
-
-
-
-
- Preview
-
-
-
-
- Scope Mg/Sub
-
-
-
-
- Scope Name/Id
-
-
-
-
- hasAssignment
-
-
-
-
-
-
-
-
-
-
-
-
-JSON
-PolicySet Type
-ALZ
-Category
-Deprecated
-Preview
-Scope Mg/Sub
-Scope Name/Id
-hasAssignments
-Assignments Count
-Assignments
-
-
-
-"@)
- $htmlDefinitionInsightshlp = $null
- $htmlDefinitionInsightshlp = foreach ($policySet in ($tenantAllPolicySets | Sort-Object @{Expression = { $_.DisplayName } }, @{Expression = { $_.PolicyDefinitionId } })) {
- $hasAssignments = 'false'
- $assignmentsCount = 0
- $assignmentsDetailed = 'n/a'
-
- if (($htPolicyWithAssignments).policySet.($policySet.PolicyDefinitionId)) {
- $hasAssignments = 'true'
- $assignments = ($htPolicyWithAssignments).policySet.($policySet.PolicyDefinitionId).Assignments
- $assignmentsCount = ($assignments | Measure-Object).Count
-
- if ($assignmentsCount -gt 0) {
- $arrayAssignmentDetails = @()
- $arrayAssignmentDetails = foreach ($assignment in $assignments) {
- if ($assignment.PolicyAssignmentDisplayName -eq '') {
- $polAssDisplayName = '#no AssignmentName given '
- }
- else {
- $polAssDisplayName = $assignment.PolicyAssignmentDisplayName
- }
- "$($assignment.PolicyAssignmentId) ($($polAssDisplayName) )"
- }
- $assignmentsDetailed = $arrayAssignmentDetails -join "$CsvDelimiterOpposite "
- }
- }
-
- $scopeDetails = 'n/a'
- if ($policySet.ScopeId -ne 'n/a') {
- $scopeDetails = "$($policySet.ScopeId) ($($htEntities.($policySet.ScopeId).DisplayName))"
- }
- $json = $($policySet.Json | ConvertTo-Json -Depth 99)
- $guid = ([System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($policySet.PolicyDefinitionId)))) -replace '-'
- @"
-
-
-
-
- Copy definition
-
-
-
-
-
-
-
-
-$($policySet.Type)
-$($policySet.ALZ)
-$($policySet.Category -replace '<', '<' -replace '>', '>')
-$($policySet.Deprecated)
-$($policySet.Preview)
-$($policySet.ScopeMgSub)
-$($scopeDetails -replace '<', '<' -replace '>', '>')
-$hasAssignments
-$assignmentsCount
-$assignmentsDetailed
-
-"@
- }
- [void]$htmlDefinitionInsights.AppendLine($htmlDefinitionInsightshlp)
- if ($NoDefinitionInsightsDedicatedHTML) {
- $htmlDefinitionInsights | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $htmlDefinitionInsights = [System.Text.StringBuilder]::new()
- }
- [void]$htmlDefinitionInsights.AppendLine( @"
-
-
-
-
-
-"@)
- $endDefinitionInsightsPolicySetDefinitions = Get-Date
- Write-Host " DefinitionInsights PolicySet definitions duration: $((New-TimeSpan -Start $startDefinitionInsightsPolicySetDefinitions -End $endDefinitionInsightsPolicySetDefinitions).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefinitionInsightsPolicySetDefinitions -End $endDefinitionInsightsPolicySetDefinitions).TotalSeconds) seconds)"
- showMemoryUsage
- #endregion definitionInsightsPolicySetDefinitions
-
- [void]$htmlDefinitionInsights.AppendLine( @'
-
-'@)
- #endregion definitionInsightsAzurePolicy
-
- #region definitionInsightsAzureRBAC
- [void]$htmlDefinitionInsights.AppendLine( @'
-
-
-'@)
-
- #RBAC preQuery
- $htRoleWithAssignments = @{}
- foreach ($roleDef in $rbacAll | Sort-Object -Property RoleAssignmentId -Unique | Group-Object -Property RoleId) {
- if (-not ($htRoleWithAssignments).($roleDef.Name)) {
- ($htRoleWithAssignments).($roleDef.Name) = @{}
- ($htRoleWithAssignments).($roleDef.Name).Assignments = $roleDef.group
- }
- }
-
- #region definitionInsightsRoleDefinitions
- $startDefinitionInsightsRoleDefinitions = Get-Date
- Write-Host ' processing DefinitionInsights Role definitions'
- ShowMemoryUsage
- $tfCount = $tenantAllRolesCount
- $htmlTableId = 'definitionInsights_Roles'
- [void]$htmlDefinitionInsights.AppendLine( @"
-
$tenantAllRolesCount Role definitions
-
-
-
-
-
- Search JSON
-
-
-
-
- Builtin/Custom
-
-
-
-
- Data
-
-
-
-
- canDoRoleAssignments
-
-
-
-
- hasAssignment
-
-
-
-
-
-
-
-
-
-
-
-
-JSON
-Role Type
-Data
-canDoRoleAssignments
-hasAssignments
-Assignments Count
-Assignments
-
-
-
-"@)
- $arrayRoleDefinitionsForCSVExport = [System.Collections.ArrayList]@()
- $htmlDefinitionInsightshlp = $null
- $htmlDefinitionInsightshlp = foreach ($role in ($tenantAllRoles | Sort-Object @{Expression = { $_.Name } })) {
- if ($role.IsCustom -eq $true) {
- $roleType = 'Custom'
- $AssignableScopesCount = $role.AssignableScopes.Count
- if ($role.AssignableScopes -like '*/providers/microsoft.management/managementgroups/*') {
- $AssignableScopesMG = $true
- }
- else {
- $AssignableScopesMG = $false
- }
-
- }
- else {
- $roleType = 'Builtin'
- $AssignableScopesCount = ''
- $AssignableScopesMG = ''
- }
- if (-not [string]::IsNullOrEmpty($role.DataActions) -or -not [string]::IsNullOrEmpty($role.NotDataActions)) {
- $roleManageData = 'true'
- }
- else {
- $roleManageData = 'false'
- }
-
- $hasAssignments = 'false'
- $assignmentsCount = 0
- $assignmentsDetailed = 'n/a'
- if (($htRoleWithAssignments).($role.Id)) {
- $hasAssignments = 'true'
- $assignments = ($htRoleWithAssignments).($role.Id).Assignments
- $assignmentsCount = ($assignments).Count
- if ($assignmentsCount -gt 0) {
- $arrayAssignmentDetails = @()
- $arrayAssignmentDetails = foreach ($assignment in $assignments) {
- "$($assignment.RoleAssignmentId)"
- }
- $assignmentsDetailed = $arrayAssignmentDetails -join "$CsvDelimiterOpposite "
- }
- }
-
- #array for exportCSV
- if (-not $NoCsvExport) {
- $null = $arrayRoleDefinitionsForCSVExport.Add([PSCustomObject]@{
- Name = $role.Name
- Id = $role.Id
- Description = $role.Json.description
- Type = $roleType
- AssignmentsCount = $assignmentsCount
- AssignableScopesCount = $AssignableScopesCount
- AssignableScopesMG = $AssignableScopesMG
- AssignableScopes = ($role.AssignableScopes | Sort-Object) -join "$CsvDelimiterOpposite "
- DataRelated = $roleManageData
- RoleAssWriteCapable = $role.RoleCanDoRoleAssignments
- Actions = $role.Actions -join "$CsvDelimiterOpposite "
- NotActions = $role.NotActions -join "$CsvDelimiterOpposite "
- DataActions = $role.DataActions -join "$CsvDelimiterOpposite "
- NotDataActions = $role.NotDataActions -join "$CsvDelimiterOpposite "
- })
- }
-
- $json = $role.Json | ConvertTo-Json -Depth 99
- $guid = $role.Id -replace '-'
- @"
-
-
-
-
- Copy definition
-
-
-
-
-
-
-
-
-$($roleType)
-$($roleManageData)
-$($role.RoleCanDoRoleAssignments)
-$hasAssignments
-$assignmentsCount
-$assignmentsDetailed
-
-"@
- }
-
- #region exportCSV
- if (-not $NoCsvExport) {
- $csvFilename = "$($filename)_RoleDefinitions"
- Write-Host " Exporting RoleDefinitions CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
- $arrayRoleDefinitionsForCSVExport | Sort-Object -Property Type, Name, Id | Export-Csv -Encoding utf8 -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter $csvDelimiter -NoTypeInformation
- $arrayRoleDefinitionsForCSVExport = $null
- }
- #endregion exportCSV
-
- [void]$htmlDefinitionInsights.AppendLine($htmlDefinitionInsightshlp)
- if ($NoDefinitionInsightsDedicatedHTML) {
- $htmlDefinitionInsights | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $htmlDefinitionInsights = [System.Text.StringBuilder]::new()
- }
- [void]$htmlDefinitionInsights.AppendLine( @"
-
-
-
-
-
-"@)
- $endDefinitionInsightsRoleDefinitions = Get-Date
- Write-Host " DefinitionInsights Role definitions duration: $((New-TimeSpan -Start $startDefinitionInsightsRoleDefinitions -End $endDefinitionInsightsRoleDefinitions).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefinitionInsightsRoleDefinitions -End $endDefinitionInsightsRoleDefinitions).TotalSeconds) seconds)"
- showMemoryUsage
- #endregion definitionInsightsRoleDefinitions
-
- [void]$htmlDefinitionInsights.AppendLine( @'
-
-'@)
- #endregion definitionInsightsAzureRBAC
-
- Write-Host " NoDefinitionInsightsDedicatedHTML: $NoDefinitionInsightsDedicatedHTML"
- if ($NoDefinitionInsightsDedicatedHTML) {
- Write-Host ' Appending DefinitionInsights to HTML'
- $script:html += $htmlDefinitionInsights
- $htmlDefinitionInsights = $null
- $script:html | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $script:html = $null
- }
- else {
- Write-Host " Creating dedicated DefinitionInsights HTML ($($outputPath)$($DirectorySeparatorChar)$($fileName)_DefinitionInsights.html)"
- $htmlDefinitionInsightsDedicated = $null
- $htmlDefinitionInsightsDedicated += $htmlDefinitionInsightsDedicatedStart
- $htmlDefinitionInsightsDedicated += $htmlDefinitionInsights
- $htmlDefinitionInsightsDedicated += $htmlDefinitionInsightsDedicatedEnd
- #$htmlDefinitionInsights = $null
- $htmlDefinitionInsightsDedicated | Set-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_DefinitionInsights.html" -Encoding utf8 -Force
- #$script:htmlDefinitionInsightsDedicated = $null
-
- $htmlDefinitionInsightsNo = @"
- DefinitionInsights has been saved to dedicated HTML file '$($outputPathGiven)$($DirectorySeparatorChar)$($fileName)_DefinitionInsights.html ' (parameter -NoDefinitionInsightsDedicatedHTML = $($NoDefinitionInsightsDedicatedHTML))
- Open DefinitionInsights
-"@
- $script:html += $htmlDefinitionInsightsNo
- #$htmlDefinitionInsightsNo = $null
- $script:html | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $script:html = $null
- }
-
-
- $endDefinitionInsights = Get-Date
- Write-Host " DefinitionInsights processing duration: $((New-TimeSpan -Start $startDefinitionInsights -End $endDefinitionInsights).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefinitionInsights -End $endDefinitionInsights).TotalSeconds) seconds)"
- ShowMemoryUsage
-}
-function processDiagramMermaid() {
- if ($ManagementGroupId -ne $azAPICallConf['checkContext'].Tenant.Id) {
- $optimizedTableForPathQueryMg = $optimizedTableForPathQueryMg.where({ $_.mgParentId -ne "'upperScopes'" })
- }
- $mgLevels = ($optimizedTableForPathQueryMg | Sort-Object -Property Level -Unique).Level
-
- foreach ($mgLevel in $mgLevels) {
- $mgsInLevel = ($optimizedTableForPathQueryMg.where( { $_.Level -eq $mgLevel } )).MgId | Get-Unique
- foreach ($mgInLevel in $mgsInLevel) {
- $mgDetails = ($optimizedTableForPathQueryMg.where( { $_.Level -eq $mgLevel -and $_.MgId -eq $mgInLevel } ))
- $mgName = $mgDetails.MgName | Get-Unique
- $mgParentId = $mgDetails.mgParentId | Get-Unique
- $mgParentName = $mgDetails.mgParentName | Get-Unique
- if ($mgInLevel -ne $getMgParentId) {
- $null = $script:arrayMgs.Add($mgInLevel)
- }
-
- if ($mgParentName -eq $mgParentId) {
- $mgParentNameId = $mgParentName
- }
- else {
- $mgParentNameId = "$mgParentName $mgParentId"
- }
-
- if ($mgName -eq $mgInLevel) {
- $mgNameId = $mgName
- }
- else {
- $mgNameId = "$mgName $mgInLevel"
- }
- $script:markdownhierarchyMgs += @"
-$mgParentId(`"$mgParentNameId`") --> $mgInLevel(`"$mgNameId`")`n
-"@
- $subsUnderMg = ($optimizedTableForPathQueryMgAndSub.where( { -not [string]::IsNullOrEmpty($_.SubscriptionId) -and $_.Level -eq $mgLevel -and $_.MgId -eq $mgInLevel } )).SubscriptionId
- if (($subsUnderMg | Measure-Object).count -gt 0) {
- foreach ($subUnderMg in $subsUnderMg) {
- $null = $script:arraySubs.Add("SubsOf$mgInLevel")
- $mgDetalsN = ($optimizedTableForPathQueryMg.where( { $_.Level -eq $mgLevel -and $_.MgId -eq $mgInLevel } ))
- $mgName = $mgDetalsN.MgName | Get-Unique
- $mgParentId = $mgDetalsN.MgParentId | Get-Unique
- $mgParentName = $mgDetalsN.MgParentName | Get-Unique
- $subName = ($optimizedTableForPathQuery.where( { $_.Level -eq $mgLevel -and $_.MgId -eq $mgInLevel -and $_.SubscriptionId -eq $subUnderMg } )).Subscription | Get-Unique
- $script:markdownTable += @"
-| $mgLevel | $mgName | $mgInLevel | $mgParentName | $mgParentId | $subName | $($subUnderMg -replace '.*/') |`n
-"@
- }
- $mgName = ($optimizedTableForPathQueryMg.where( { $_.Level -eq $mgLevel -and $_.MgId -eq $mgInLevel } )).MgName | Get-Unique
- if ($mgName -eq $mgInLevel) {
- $mgNameId = $mgName
- }
- else {
- $mgNameId = "$mgName $mgInLevel"
- }
- $script:markdownhierarchySubs += @"
-$mgInLevel(`"$mgNameId`") --> SubsOf$mgInLevel(`"$(($subsUnderMg | Measure-Object).count)`")`n
-"@
- }
- else {
- $mgDetailsM = ($optimizedTableForPathQueryMg.where( { $_.Level -eq $mgLevel -and $_.MgId -eq $mgInLevel } ))
- $mgName = $mgDetailsM.MgName | Get-Unique
- $mgParentId = $mgDetailsM.MgParentId | Get-Unique
- $mgParentName = $mgDetailsM.MgParentName | Get-Unique
- $script:markdownTable += @"
-| $mgLevel | $mgName | $mgInLevel | $mgParentName | $mgParentId | none | none |`n
-"@
- }
-
- if (($script:outOfScopeSubscriptions | Measure-Object).count -gt 0) {
- $subsoosUnderMg = ($outOfScopeSubscriptions | Where-Object { $_.Level -eq $mgLevel -and $_.ManagementGroupId -eq $mgInLevel }).SubscriptionId | Get-Unique
- if (($subsoosUnderMg | Measure-Object).count -gt 0) {
- foreach ($subUnderMg in $subsoosUnderMg) {
- $null = $script:arraySubsOos.Add("SubsoosOf$mgInLevel")
- $mgDetalsN = ($optimizedTableForPathQueryMg.where( { $_.Level -eq $mgLevel -and $_.ManagementGroupId -eq $mgInLevel } ))
- $mgName = $mgDetalsN.MgName | Get-Unique
- }
- $mgName = ($outOfScopeSubscriptions | Where-Object { $_.Level -eq $mgLevel -and $_.ManagementGroupId -eq $mgInLevel }).ManagementGroupName | Get-Unique
- if ($mgName -eq $mgInLevel) {
- $mgNameId = $mgName
- }
- else {
- $mgNameId = "$mgName $mgInLevel"
- }
- $script:markdownhierarchySubs += @"
-$mgInLevel(`"$mgNameId`") --> SubsoosOf$mgInLevel(`"$(($subsoosUnderMg | Measure-Object).count)`")`n
-"@
- }
- }
- }
- }
-}
-function processHierarchyMapOnly {
- foreach ($entity in $htEntities.values) {
- if ($entity.parentNameChain -contains $ManagementGroupID -or $entity.Id -eq $ManagementGroupId) {
-
- if ($entity.type -eq '/subscriptions') {
- $hlpEntityParent = $htEntities.(($entity.parent))
- addRowToTable `
- -level (($entity.ParentNameChain).Count - 1) `
- -mgName $hlpEntityParent.displayName `
- -mgId ($entity.parent) `
- -mgParentId $hlpEntityParent.Parent `
- -mgParentName $hlpEntityParent.ParentDisplayName `
- -Subscription $entity.DisplayName `
- -SubscriptionId $entity.Id
- }
- if ($entity.type -eq 'Microsoft.Management/managementGroups') {
- addRowToTable `
- -level ($entity.ParentNameChain).Count `
- -mgName $entity.displayname `
- -mgId $entity.id `
- -mgParentId $entity.Parent `
- -mgParentName $entity.ParentDisplayName
- }
- }
- }
-}
-function processHierarchyMapOnlyCustomData {
- Write-Host 'HierarchyMapOnly with custom data' -ForegroundColor Yellow
- Write-Host ' Parameter HierarchyMapOnly:' $HierarchyMapOnly
- Write-Host ' Check if HierarchyMapOnlyCustomDataJSON is valid JSON'
- try {
- $HierarchyMapOnlyCustomDataConvertedAsHashTable = $HierarchyMapOnlyCustomDataJSON | ConvertFrom-Json -AsHashtable
- $hierarchyMapOnlyCustomData = @{}
- foreach ($key in $HierarchyMapOnlyCustomDataConvertedAsHashTable.Keys) {
- $hierarchyMapOnlyCustomData.$key = $HierarchyMapOnlyCustomDataConvertedAsHashTable.$key | ConvertTo-Json | ConvertFrom-Json
- }
- Write-Host ' HierarchyMapOnlyCustomDataJSON is valid JSON' -ForegroundColor Green
- }
- catch {
- throw 'HierarchyMapOnlyCustomDataJSON is not valid JSON'
- }
-
- Write-Host ' Parameter hierarchyMapOnlyCustomData count:' $hierarchyMapOnlyCustomData.Keys.Count
-
- #validate
- Write-Host ' ManagementGroupId validation'
- if (-not $ManagementGroupId) {
- throw 'ManagementGroupId validation failed - please provide ManagementGroupId (parameter -ManagementGroupId)'
- }
- else {
- if ($hierarchyMapOnlyCustomData.$ManagementGroupId) {
- Write-Host " ManagementGroupId '$ManagementGroupId' is available in 'hierarchyMapOnlyCustomData'"
- }
- else {
- throw "ManagementGroupId validation failed - Given ManagementGroupId '$ManagementGroupId' is NOT available in 'hierarchyMapOnlyCustomData'"
- }
- Write-Host " ManagementGroupId validation passed '$ManagementGroupId'" -ForegroundColor Green
- }
-
- Write-Host ' CustomData validation'
- if ($hierarchyMapOnlyCustomData.Keys.Count -gt 0) {
- Write-Host ' Checking Keys (sanity check on first item)'
- $requiredKeys = @('Id', 'ParentId', 'ParentNameChain', 'ParentDisplayName', 'DisplayName', 'type')
- $firstItem = $hierarchyMapOnlyCustomData.($($hierarchyMapOnlyCustomData.Keys)[0])
- foreach ($requiredKey in $requiredKeys) {
- if (($firstitem | Get-Member -Name $requiredKey)) {
- Write-Host " Key:$($requiredKey) exists" -ForegroundColor Green
- }
- else {
- Write-Host " CustomData validation failed - required key:$($requiredKey) missing" -ForegroundColor DarkRed
- Write-Host " The following keys are expected: $($requiredKeys -join ', ')"
- throw "CustomData validation failed - required key:$($requiredKey) missing"
- }
- }
-
- Write-Host ' Checking for existence of Management Groups'
- $HierarchyMapOnlyCustomDataHroupedByType = $hierarchyMapOnlyCustomData.values | Group-Object -Property type
- if ($HierarchyMapOnlyCustomDataHroupedByType.Name -notcontains 'Microsoft.Management/managementGroups') {
- Write-Host ' CustomData validation failed - Custom data does not contain Manangement Groups'
- throw 'CustomData validation failed - Custom data does not contain Manangement Groups'
- }
- else {
- Write-Host ' Checking for existence of Management Groups passed' -ForegroundColor Green
- }
- foreach ($type in $HierarchyMapOnlyCustomDataHroupedByType) {
- Write-Host " Custom Data contains $($type.Count) x type: '$($type.name)'"
- }
-
- Write-Host ' CustomData validation passed' -ForegroundColor Green
- }
- else {
- Write-Host " CustomData validation failed - no data (`$hierarchyMapOnlyCustomData.Keys.Count: $($hierarchyMapOnlyCustomData.Keys.Count))"
- throw "CustomData validation failed - no data (`$hierarchyMapOnlyCustomData.Keys.Count: $($hierarchyMapOnlyCustomData.Keys.Count))"
- }
- $script:htEntities = $hierarchyMapOnlyCustomData
-}
-function processManagedIdentities {
- Write-Host 'Processing Service Principals - Managed Identities'
- $startSPMI = Get-Date
- $script:servicePrincipalsOfTypeManagedIdentity = $htServicePrincipals.Keys.where( { $htServicePrincipals.($_).servicePrincipalType -eq 'ManagedIdentity' } )
- $script:servicePrincipalsOfTypeManagedIdentityCount = $servicePrincipalsOfTypeManagedIdentity.Count
- if ($servicePrincipalsOfTypeManagedIdentityCount -gt 0) {
- foreach ($sp in $servicePrincipalsOfTypeManagedIdentity) {
- $hlpSp = $htServicePrincipals.($sp)
- if ($hlpSp.alternativeNames -gt 0) {
- foreach ($usageentry in $hlpSp.alternativeNames) {
- if ($usageentry -like '*/providers/Microsoft.Authorization/policyAssignments/*') {
- $script:htManagedIdentityForPolicyAssignment.($hlpSp.Id) = @{}
- $script:htManagedIdentityForPolicyAssignment.($hlpSp.Id).policyAssignmentId = $usageentry.ToLower()
- $script:htPolicyAssignmentManagedIdentity.($usageentry.ToLower()) = @{}
- $script:htPolicyAssignmentManagedIdentity.($usageentry.ToLower()).miObjectId = $hlpSp.id
- if (-not $htManagedIdentityDisplayName.($hlpSp.displayName)) {
- $script:htManagedIdentityDisplayName.("$($hlpSp.displayName)_$($usageentry.ToLower())") = $hlpSp
- }
- }
- }
- }
- }
- }
- $endSPMI = Get-Date
- Write-Host "Processing Service Principals - Managed Identities duration: $((New-TimeSpan -Start $startSPMI -End $endSPMI).TotalMinutes) minutes ($((New-TimeSpan -Start $startSPMI -End $endSPMI).TotalSeconds) seconds)"
-}
-function processNetwork {
- $start = Get-Date
- Write-Host "Processing Network enrichment ($($arrayVNets.Count) Virtual Networks)"
-
- $htVNets = @{}
- foreach ($vnet in $arrayVNets) {
- $htVNets.($vnet.id) = $vnet
- }
-
- $script:htSubnets = @{}
- $script:arrayVirtualNetworks = [System.Collections.ArrayList]@()
- $script:arraySubnets = [System.Collections.ArrayList]@()
-
- foreach ($vnet in $arrayVNets) {
-
- #region peerings
- $vnetIdSplit = ($vnet.id -split '/')
- $subscriptionId = $vnetIdSplit[2]
-
- $subscriptionName = 'n/a'
- $MGPath = 'n/a'
- if ($htSubscriptionsMgPath.($subscriptionId)) {
- $subHelper = $htSubscriptionsMgPath.($subscriptionId)
- $subscriptionName = $subHelper.displayName
- $MGPath = $subHelper.ParentNameChainDelimited
- }
-
- $subnetsWithPrivateEndPointsCount = 0
- if ($vnet.properties.subnets.properties.privateEndpoints.id.Count -gt 0) {
- $subnetsWithPrivateEndPointsCount = $vnet.properties.subnets.where({ $_.properties.privateEndpoints.id.Count -gt 0 }).Count
- }
-
- $subnetsWithConnectedDevicesCount = 0
- if ($vnet.properties.subnets.properties.ipConfigurations.id.Count -gt 0) {
- $subnetsWithConnectedDevicesCount = $vnet.properties.subnets.where({ $_.properties.ipConfigurations.id.Count -gt 0 }).Count
- }
-
- $vnetResourceGroup = $vnetIdSplit[4]
- if ($vnet.properties.virtualNetworkPeerings.id.Count -gt 0) {
- foreach ($peering in $vnet.properties.virtualNetworkPeerings) {
- $remotevnetIdSplit = ($peering.properties.remoteVirtualNetwork.id -split '/')
- $remotesubscriptionId = $remotevnetIdSplit[2]
-
- $remotesubscriptionName = 'n/a'
- $remoteMGPath = 'n/a'
- $peeringXTenant = 'unknown'
- if ($htSubscriptionsMgPath.($remotesubscriptionId)) {
- $peeringXTenant = 'false'
- $remotesubHelper = $htSubscriptionsMgPath.($remotesubscriptionId)
- $remotesubscriptionName = $remotesubHelper.displayName
- $remoteMGPath = $remotesubHelper.ParentNameChainDelimited
- }
- else {
- if ($htUnknownTenantsForSubscription.($remotesubscriptionId)) {
- $remoteTenantId = $htUnknownTenantsForSubscription.($remotesubscriptionId).TenantId
- $remoteMGPath = $remoteTenantId
- if ($remoteTenantId -eq $azApiCallConf['checkcontext'].tenant.id) {
- $peeringXTenant = 'false'
- }
- else {
- $peeringXTenant = 'true'
- }
- }
- else {
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($remotesubscriptionId)?api-version=2020-01-01"
- $remoteTenantId = AzAPICall -AzAPICallConfiguration $azApiCallConf -uri $uri -listenOn 'content' -currentTask "getTenantId for subscriptionId '$($remotesubscriptionId)'"
- if ($remoteTenantId.id -like '/subscriptions/*') {
- #sub actually could be resolved but not available in htSubscriptionsMgPath
- Write-Host "SubscriptionId '$($remotesubscriptionId)' (tenantId: '$($remoteTenantId.tenantId)' (current context tenantId: '$($azapiCallConf['checkContext'].tenant.Id)')) was not captured by getSubscriptions/getEntities, however could be fully resolved with direct get call (ARM subscription API)" -ForegroundColor Magenta
- $remoteMGPath = $remoteTenantId.tenantId
- if ($azapiCallConf['checkContext'].tenant.Id -eq $remoteTenantId.tenantId) {
- $peeringXTenant = 'false'
- }
- else {
- $peeringXTenant = 'true'
- }
- }
- else {
- $arrayRemoteMGPath = @()
- foreach ($remoteId in $remoteTenantId) {
- if ($remoteId -eq 'SubscriptionNotFound Tenant unknown') {
- $remoteMGPath = 'unknown'
- $peeringXTenant = 'n/a'
- }
- else {
- $objectGuid = [System.Guid]::empty
- if ([System.Guid]::TryParse($remoteId, [System.Management.Automation.PSReference]$ObjectGuid)) {
- if ($remoteId -in $MSTenantIds) {
- $arrayRemoteMGPath += "$remoteId (MS)"
- }
- else {
- $arrayRemoteMGPath += $remoteId
- }
- if ($remoteId -eq $azApiCallConf['checkcontext'].tenant.id) {
- $peeringXTenant = 'false'
- }
- else {
- $peeringXTenant = 'true'
- }
- }
- $script:htUnknownTenantsForSubscription.($remotesubscriptionId) = @{}
- $script:htUnknownTenantsForSubscription.($remotesubscriptionId).TenantId = $arrayRemoteMGPath -join ', '
- $remoteMGPath += $arrayRemoteMGPath -join ', '
- }
- }
- }
- }
- }
-
- $remotevnetName = $remotevnetIdSplit[8]
- $remotevnetResourceGroup = $remotevnetIdSplit[4]
-
- if ($htVNets.($peering.properties.remoteVirtualNetwork.id)) {
- $remotevnetState = 'existent'
- $remoteLocation = $htVNets.($peering.properties.remoteVirtualNetwork.id).location
- $remotePeeringsCount = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.virtualNetworkPeerings.id.Count
- $remoteDhcpoptionsDnsservers = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.dhcpoptions.dnsservers
- $remoteSubnetsCount = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.subnets.id.Count
- $remoteSubnetsWithNSGCount = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.subnets.properties.networkSecurityGroup.id.Count
- $remoteSubnetsWithRouteTable = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.subnets.properties.routeTable.id.Count
- $remoteSubnetsWithDelegations = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.subnets.properties.delegations.id.Count
- $remotePrivateEndPoints = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.subnets.properties.privateEndpoints.id.Count
- $remoteSubnetsWithPrivateEndPoints = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.subnets.where({ $_.properties.privateEndpoints.id.Count -gt 0 }).Count
- $remoteConnectedDevices = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.subnets.properties.ipConfigurations.id.Count
- $remoteSubnetsWithConnectedDevices = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.subnets.where({ $_.properties.ipConfigurations.id.Count -gt 0 }).Count
- $remoteDdosProtection = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.enableDdosProtection
- $remotePeering = $htVNets.($peering.properties.remoteVirtualNetwork.id).properties.virtualNetworkPeerings.where({ $_.properties.remoteVirtualNetwork.id -eq $vnet.id })
- if ($remotePeering.count -eq 1) {
- $remotePeeringName = $remotePeering.name
- $remotePeeringState = $remotePeering.Properties.peeringState
- $remotePeeringSyncLevel = $remotePeering.Properties.peeringSyncLevel
- $remoteAllowVirtualNetworkAccess = $remotePeering.properties.allowVirtualNetworkAccess
- $remoteAllowForwardedTraffic = $remotePeering.properties.allowForwardedTraffic
- $remoteAllowGatewayTransit = $remotePeering.properties.allowGatewayTransit
- $remoteUseRemoteGateways = $remotePeering.properties.useRemoteGateways
- $remoteDoNotVerifyRemoteGateways = $remotePeering.properties.doNotVerifyRemoteGateways
- $remotePeerCompleteVnets = $remotePeering.properties.peerCompleteVnets
- $remoteRouteServiceVips = $remotePeering.properties.routeServiceVips
- }
- else {
- $remotePeeringName = 'n/a'
- $remotePeeringState = 'n/a'
- $remotePeeringSyncLevel = 'n/a'
- $remoteAllowVirtualNetworkAccess = 'n/a'
- $remoteAllowForwardedTraffic = 'n/a'
- $remoteAllowGatewayTransit = 'n/a'
- $remoteUseRemoteGateways = 'n/a'
- $remoteDoNotVerifyRemoteGateways = 'n/a'
- $remotePeerCompleteVnets = 'n/a'
- $remoteRouteServiceVips = 'n/a'
- }
-
- }
- else {
- if ($getMgParentName -eq 'Tenant Root') {
- $remotevnetState = 'non-existent'
- }
- else {
- $remotevnetState = 'n/a'
- }
- $remoteLocation = 'n/a'
- $remotePeeringsCount = 'n/a'
- $remoteDhcpoptionsDnsservers = 'n/a'
- $remoteSubnetsCount = 'n/a'
- $remoteSubnetsWithNSGCount = 'n/a'
- $remoteSubnetsWithRouteTable = 'n/a'
- $remoteSubnetsWithDelegations = 'n/a'
- $remotePrivateEndPoints = 'n/a'
- $remoteSubnetsWithPrivateEndPoints = 'n/a'
- $remoteConnectedDevices = 'n/a'
- $remoteSubnetsWithConnectedDevices = 'n/a'
- $remoteDdosProtection = 'n/a'
- $remotePeeringName = 'n/a'
- $remotePeeringState = 'n/a'
- $remotePeeringSyncLevel = 'n/a'
- $remoteAllowVirtualNetworkAccess = 'n/a'
- $remoteAllowForwardedTraffic = 'n/a'
- $remoteAllowGatewayTransit = 'n/a'
- $remoteUseRemoteGateways = 'n/a'
- $remoteDoNotVerifyRemoteGateways = 'n/a'
- $remotePeerCompleteVnets = 'n/a'
- $remoteRouteServiceVips = 'n/a'
- }
-
- $null = $script:arrayVirtualNetworks.Add([PSCustomObject]@{
- SubscriptionName = $subscriptionName
- Subscription = ($vnet.id -split '/')[2]
- MGPath = $MGPath
- VNet = $vnet.name
- VNetId = $vnet.id
- VNetResourceGroup = $vnetResourceGroup
- Location = $vnet.location
- AddressSpaceAddressPrefixes = ($vnet.properties.addressSpace.addressPrefixes -join "$CsvDelimiterOpposite ")
- DhcpoptionsDnsservers = ($vnet.properties.dhcpoptions.dnsservers -join "$CsvDelimiterOpposite ")
- SubnetsCount = $vnet.properties.subnets.id.Count
- SubnetsWithNSGCount = $vnet.properties.subnets.properties.networkSecurityGroup.id.Count
- SubnetsWithRouteTableCount = $vnet.properties.subnets.properties.routeTable.id.Count
- SubnetsWithDelegationsCount = $vnet.properties.subnets.properties.delegations.id.Count
- PrivateEndpointsCount = $vnet.properties.subnets.properties.privateEndpoints.id.Count
- SubnetsWithPrivateEndPointsCount = $subnetsWithPrivateEndPointsCount
- ConnectedDevices = $vnet.properties.subnets.properties.ipConfigurations.id.Count
- SubnetsWithConnectedDevicesCount = $subnetsWithConnectedDevicesCount
- DdosProtection = $vnet.properties.enableDdosProtection
-
- PeeringsCount = $vnet.properties.virtualNetworkPeerings.id.Count
- PeeringXTenant = $peeringXTenant
- PeeringName = $peering.name
- PeeringState = $peering.properties.peeringState
- PeeringSyncLevel = $peering.properties.peeringSyncLevel
- AllowVirtualNetworkAccess = $peering.properties.allowVirtualNetworkAccess
- AllowForwardedTraffic = $peering.properties.allowForwardedTraffic
- AllowGatewayTransit = $peering.properties.allowGatewayTransit
- UseRemoteGateways = $peering.properties.useRemoteGateways
- DoNotVerifyRemoteGateways = $peering.properties.doNotVerifyRemoteGateways
- PeerCompleteVnets = $peering.properties.peerCompleteVnets
- RouteServiceVips = $peering.properties.routeServiceVips
-
- RemotePeeringsCount = $remotePeeringsCount
- RemotePeeringName = $remotePeeringName
- RemotePeeringState = $remotePeeringState
- RemotePeeringSyncLevel = $remotePeeringSyncLevel
- RemoteAllowVirtualNetworkAccess = $RemoteAllowVirtualNetworkAccess
- RemoteAllowForwardedTraffic = $RemoteAllowForwardedTraffic
- RemoteAllowGatewayTransit = $RemoteAllowGatewayTransit
- RemoteUseRemoteGateways = $RemoteUseRemoteGateways
- RemoteDoNotVerifyRemoteGateways = $RemoteDoNotVerifyRemoteGateways
- RemotePeerCompleteVnets = $RemotePeerCompleteVnets
- RemoteRouteServiceVips = $RemoteRouteServiceVips
-
- RemoteSubscriptionName = $remotesubscriptionName
- RemoteSubscription = $remotesubscriptionId
- RemoteMGPath = $remoteMGPath -join ', '
- RemoteVNet = $remotevnetName
- RemoteVNetId = $peering.properties.remoteVirtualNetwork.id
- RemoteVNetState = $remotevnetState
- RemoteVNetResourceGroup = $remotevnetResourceGroup
- RemoteVNetLocation = $remoteLocation
- RemoteAddressSpaceAddressPrefixes = ($peering.properties.remoteAddressSpace.addressPrefixes -join "$CsvDelimiterOpposite ")
- RemoteVirtualNetworkAddressSpaceAddressPrefixes = ($peering.properties.remoteVirtualNetworkAddressSpace.addressPrefixes -join "$CsvDelimiterOpposite ")
-
- RemoteDhcpoptionsDnsservers = ($remoteDhcpoptionsDnsservers -join "$CsvDelimiterOpposite ")
- RemoteSubnetsCount = $remoteSubnetsCount
- RemoteSubnetsWithNSGCount = $remoteSubnetsWithNSGCount
- RemoteSubnetsWithRouteTable = $remoteSubnetsWithRouteTable
- RemoteSubnetsWithDelegations = $remoteSubnetsWithDelegations
- RemotePrivateEndPoints = $remotePrivateEndPoints
- RemoteSubnetsWithPrivateEndPoints = $remoteSubnetsWithPrivateEndPoints
- RemoteConnectedDevices = $remoteConnectedDevices
- RemoteSubnetsWithConnectedDevices = $remoteSubnetsWithConnectedDevices
- RemoteDdosProtection = $remoteDdosProtection
- })
- }
-
- }
- else {
- $null = $script:arrayVirtualNetworks.Add([PSCustomObject]@{
- SubscriptionName = $subscriptionName
- Subscription = ($vnet.id -split '/')[2]
- MGPath = $MGPath
- VNet = $vnet.name
- VNetId = $vnet.id
- VNetResourceGroup = $vnetResourceGroup
- Location = $vnet.location
-
- AddressSpaceAddressPrefixes = ($vnet.properties.addressSpace.addressPrefixes -join "$CsvDelimiterOpposite ")
- DhcpoptionsDnsservers = ($vnet.properties.dhcpoptions.dnsservers -join "$CsvDelimiterOpposite ")
- SubnetsCount = $vnet.properties.subnets.id.Count
- SubnetsWithNSGCount = $vnet.properties.subnets.properties.networkSecurityGroup.id.Count
- SubnetsWithRouteTableCount = $vnet.properties.subnets.properties.routeTable.id.Count
- SubnetsWithDelegationsCount = $vnet.properties.subnets.properties.delegations.id.Count
- PrivateEndpointsCount = $vnet.properties.subnets.properties.privateEndpoints.id.Count
- SubnetsWithPrivateEndPointsCount = $subnetsWithPrivateEndPointsCount
- ConnectedDevices = $vnet.properties.subnets.properties.ipConfigurations.id.Count
- SubnetsWithConnectedDevicesCount = $subnetsWithConnectedDevicesCount
- DdosProtection = $vnet.properties.enableDdosProtection
-
- PeeringsCount = $vnet.properties.virtualNetworkPeerings.id.Count
- PeeringXTenant = 'n/a'
- PeeringName = ''
- PeeringState = ''
- PeeringSyncLevel = ''
- AllowVirtualNetworkAccess = ''
- AllowForwardedTraffic = ''
- AllowGatewayTransit = ''
- UseRemoteGateways = ''
- DoNotVerifyRemoteGateways = ''
- PeerCompleteVnets = ''
- RouteServiceVips = ''
-
- RemotePeeringsCount = ''
- RemotePeeringName = ''
- RemotePeeringState = ''
- RemotePeeringSyncLevel = ''
- RemoteAllowVirtualNetworkAccess = ''
- RemoteAllowForwardedTraffic = ''
- RemoteAllowGatewayTransit = ''
- RemoteUseRemoteGateways = ''
- RemoteDoNotVerifyRemoteGateways = ''
- RemotePeerCompleteVnets = ''
- RemoteRouteServiceVips = ''
-
- RemoteSubscriptionName = ''
- RemoteSubscription = ''
- RemoteMGPath = ''
- RemoteVNet = ''
- RemoteVNetId = ''
- RemoteVNetState = ''
- RemoteVNetResourceGroup = ''
- RemoteVNetLocation = ''
- RemoteAddressSpaceAddressPrefixes = ''
- RemoteVirtualNetworkAddressSpaceAddressPrefixes = ''
- RemoteDhcpoptionsDnsservers = ''
- RemoteSubnetsCount = ''
- RemoteSubnetsWithNSGCount = ''
- RemoteSubnetsWithRouteTable = ''
- RemoteSubnetsWithDelegations = ''
- RemotePrivateEndPoints = ''
- RemoteSubnetsWithPrivateEndPoints = ''
- RemoteConnectedDevices = ''
- RemoteSubnetsWithConnectedDevices = ''
- RemoteDdosProtection = ''
- })
- }
- #endregion peerings
-
- #region subnets
-
- if ($vnet.properties.subnets.Count -gt 0) {
- foreach ($subnet in $vnet.properties.subnets) {
-
- $script:htSubnets.($subnet.id) = @{
- SubscriptionName = $subscriptionName
- Subscription = ($vnet.id -split '/')[2]
- MGPath = $MGPath
- VNet = $vnet.name
- VNetId = $vnet.id
- Location = $vnet.location
- ResourceGroup = $vnetResourceGroup
- }
-
- $arrayServiceEndPoints = @()
- if ($subnet.properties.serviceEndpoints.service.Count -gt 0) {
- $arrayServiceEndPoints = foreach ($serviceEndpoint in $subnet.properties.serviceEndpoints) {
- "$($serviceEndpoint.service) ($(($serviceEndpoint.locations | Sort-Object) -join ', '))"
- }
- }
-
- $delegation = ''
- if ($subnet.properties.delegations.Count -gt 0) {
- $delegation = "$($subnet.properties.delegations.properties.serviceName) ($(($subnet.properties.delegations.properties.actions | Sort-Object) -join ', '))"
- }
-
- #region IP address usage
- #https://github.com/ElanShudnow/AzureCode/blob/242b923eada55fa795b930473a50dedf14bdc409/PowerShell/AzSubnetAvailability/AzSubnetAvailability.ps1
- # Gets the mask from the IP configuration (I.e 10.0.0.0/24, turns to just "24")
-
- if (-not [string]::IsNullOrWhiteSpace($subnet.properties.addressPrefix)) {
- $AddressPrefix = $subnet.properties.addressPrefix
- $subnetNet = $AddressPrefix -replace '/.*'
- $subnetNetOutput = $subnetNet
- }
-
- #ignore IPv6
- if (-not [string]::IsNullOrWhiteSpace($subnet.properties.addressPrefixes)) {
- $arr = foreach ($entry in $subnet.properties.addressPrefixes) {
- if ($entry -match '^(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3}([01]?\d?\d|2[0-4]\d|25[0-5])\/(\d{1}|[0-2]{1}\d{1}|3[0-2])$') {
- $AddressPrefix = $entry
- $AddressPrefix -replace '/.*'
- $subnetNet = $AddressPrefix -replace '/.*'
- }
- else {
- "(ignoring IPv6 $entry)"
- }
- }
- $subnetNetOutput = $arr
- }
-
- $Mask = $AddressPrefix.substring($AddressPrefix.Length - 2, 2)
-
- #Amount of available IP Addresses minus the 3 IPs that Azure consumes, minus net and broadcast
- #https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-faq#are-there-any-restrictions-on-using-ip-addresses-within-these-subnets
- switch ($Mask) {
- '30' { $AvailableAddresses = [Math]::Pow(2, 2) - 5 }
- '29' { $AvailableAddresses = [Math]::Pow(2, 3) - 5 }
- '28' { $AvailableAddresses = [Math]::Pow(2, 4) - 5 }
- '27' { $AvailableAddresses = [Math]::Pow(2, 5) - 5 }
- '26' { $AvailableAddresses = [Math]::Pow(2, 6) - 5 }
- '25' { $AvailableAddresses = [Math]::Pow(2, 7) - 5 }
- '24' { $AvailableAddresses = [Math]::Pow(2, 8) - 5 }
- '23' { $AvailableAddresses = [Math]::Pow(2, 9) - 5 }
- '22' { $AvailableAddresses = [Math]::Pow(2, 10) - 5 }
- '21' { $AvailableAddresses = [Math]::Pow(2, 11) - 5 }
- '20' { $AvailableAddresses = [Math]::Pow(2, 12) - 5 }
- '19' { $AvailableAddresses = [Math]::Pow(2, 13) - 5 }
- '18' { $AvailableAddresses = [Math]::Pow(2, 14) - 5 }
- '17' { $AvailableAddresses = [Math]::Pow(2, 15) - 5 }
- '16' { $AvailableAddresses = [Math]::Pow(2, 16) - 5 }
- '15' { $AvailableAddresses = [Math]::Pow(2, 17) - 5 }
- '14' { $AvailableAddresses = [Math]::Pow(2, 18) - 5 }
- '13' { $AvailableAddresses = [Math]::Pow(2, 19) - 5 }
- '12' { $AvailableAddresses = [Math]::Pow(2, 20) - 5 }
- '11' { $AvailableAddresses = [Math]::Pow(2, 21) - 5 }
- '10' { $AvailableAddresses = [Math]::Pow(2, 22) - 5 }
- '9' { $AvailableAddresses = [Math]::Pow(2, 23) - 5 }
- '8' { $AvailableAddresses = [Math]::Pow(2, 24) - 5 }
- }
-
- $IPsLeft = $AvailableAddresses - $subnet.properties.ipConfigurations.Count
- $PercentIPsUsed = [math]::Round((($subnet.properties.ipConfigurations.Count / $AvailableAddresses) * 100), 1)
- $subnetIPAddressUsageCritical = $false
- if ($PercentIPsUsed -gt $NetworkSubnetIPAddressUsageCriticalPercentage) {
- $subnetIPAddressUsageCritical = $true
- }
-
- #endregion IP address usage
-
- $subnetPrefix = $AddressPrefix -replace '.*/'
-
- $subnetmask = ([IPAddress]"$([system.convert]::ToInt64(('1'*$subnetPrefix).PadRight(32,'0'),2))").IPAddressToString
- $IPBits = [int[]]$subnetNet.Split('.')
- $MaskBits = [int[]]$subnetmask.Split('.')
- $NetworkIDBits = 0..3 | ForEach-Object { $IPBits[$_] -band $MaskBits[$_] }
- $Broadcast = (0..3 | ForEach-Object { $NetworkIDBits[$_] + ($MaskBits[$_] -bxor 255) }) -join '.'
- $Range = "$subnetNet - $Broadcast"
-
- $null = $script:arraySubnets.Add([PSCustomObject]@{
- SubscriptionName = $subscriptionName
- Subscription = ($vnet.id -split '/')[2]
- MGPath = $MGPath
- VNet = $vnet.name
- VNetId = $vnet.id
- VNetResourceGroup = $vnetResourceGroup
- Location = $vnet.location
- SubnetName = $subnet.name
- SubnetId = $subnet.id
- SubnetNet = $subnetNetOutput -join "$CsvDelimiterOpposite "
- SubnetPrefix = $subnetPrefix
- Subnetmask = $subnetmask
- Range = $Range
- ConnectedDevices = $subnet.properties.ipConfigurations.Count
- AvailableIPAddresses = $IPsLeft
- UsedIPAddressesPercent = "$PercentIPsUsed %"
- SubnetIPAddressUsageCritical = $subnetIPAddressUsageCritical
- PrivateEndpointNetworkPolicies = $subnet.properties.privateEndpointNetworkPolicies
- PrivateLinkServiceNetworkPolicies = $subnet.properties.privateLinkServiceNetworkPolicies
- ServiceEndpointsCount = $subnet.properties.serviceEndpoints.service.Count
- ServiceEndpoints = $arrayServiceEndPoints -join ', '
- Delegation = $delegation
- NetworkSecurityGroup = $subnet.properties.networkSecurityGroup.id
- RouteTable = $subnet.properties.routeTable
- NatGateway = ''
- PrivateEndpoints = $subnet.properties.privateEndpoints.Count
- })
- }
- }
- #endregion subnets
- }
-
- $end = Get-Date
- Write-Host " Processing Network enrichment duration: $((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds"
-}
-function processPrivateEndpoints {
- $start = Get-Date
- Write-Host 'Processing Private Endpoints enrichment'
-
- $script:arrayPrivateEndpointsEnriched = [System.Collections.ArrayList]@()
-
- if ($arrayPrivateEndPointsFromResourceProperties.Count -gt 0) {
- $privateEndPointsFromResourcePropertiesToProcess = ($arrayPrivateEndPointsFromResourceProperties.where({ $arrayPrivateEndPoints.id -notcontains $_.privateEndpointConnection.Properties.privateEndpoint.id }))
- $privateEndPointsFromResourcePropertiesToProcessCount = $privateEndPointsFromResourcePropertiesToProcess.Count
- Write-Host " Processing Private Endpoints enrichment for $privateEndPointsFromResourcePropertiesToProcessCount Private Endpoint(s) where the Private Endpoint was not returned from the PE API endpoint but from a resource property"
- if ($privateEndPointsFromResourcePropertiesToProcessCount -gt 0) {
- foreach ($entry in $privateEndPointsFromResourcePropertiesToProcess) {
- $peResIdSplit = $entry.privateEndpointConnection.Properties.privateEndpoint.id -split '/'
- $crossSubscriptionPE = 'n/a'
- $peSubscriptionId = $peResIdSplit[2]
- if ($peSubscriptionId -ne $entry.ResourceSubscriptionId) {
- $crossSubscriptionPE = $true
- }
- else {
- $crossSubscriptionPE = $false
- }
-
- $peMGPath = 'n/a'
- $peXTenant = 'unknown'
- if ($htSubscriptionsMgPath.($peSubscriptionId)) {
- $peMGPath = $htSubscriptionsMgPath.($peSubscriptionId).pathDelimited
- $peXTenant = $false
- }
- elseif ($htUnknownTenantsForSubscription.($peSubscriptionId)) {
- $remoteTenantId = $htUnknownTenantsForSubscription.($peSubscriptionId).TenantId
- $peMGPath = $remoteTenantId
- if ($remoteTenantId -eq $azApiCallConf['checkcontext'].tenant.id) {
- $peXTenant = $false
- }
- else {
- $peXTenant = $true
- }
- }
- else {
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($peSubscriptionId)?api-version=2020-01-01"
- $remoteTenantId = AzAPICall -AzAPICallConfiguration $azApiCallConf -uri $uri -listenOn 'content' -currentTask "getTenantId for subscriptionId '$($peSubscriptionId)'"
- $arrayRemoteMGPath = @()
- foreach ($remoteId in $remoteTenantId) {
- $objectGuid = [System.Guid]::empty
- if ([System.Guid]::TryParse($remoteId, [System.Management.Automation.PSReference]$ObjectGuid)) {
- if ($remoteId -in $MSTenantIds) {
- $arrayRemoteMGPath += "$remoteId (MS)"
- }
- else {
- $arrayRemoteMGPath += $remoteId
- }
- if ($remoteId -eq $azApiCallConf['checkcontext'].tenant.id) {
- $peXTenant = $false
- }
- else {
- $peXTenant = $true
- }
- }
- $script:htUnknownTenantsForSubscription.($peSubscriptionId) = @{}
- $script:htUnknownTenantsForSubscription.($peSubscriptionId).TenantId = $arrayRemoteMGPath -join ', '
- $peMGPath = $arrayRemoteMGPath -join ', '
- }
- }
-
- $null = $script:arrayPrivateEndpointsEnriched.Add([PSCustomObject]@{
- PEName = $entry.privateEndpointConnection.name
- PEId = $entry.privateEndpointConnection.Properties.privateEndpoint.id
- PELocation = 'n/a'
- PEResourceGroup = $peResIdSplit[4]
- PESubscriptionName = 'n/a'
- PESubscription = $peSubscriptionId
- PEMGPath = $peMGPath
- PEConnectionType = 'n/a'
- PEConnectionState = $entry.privateEndpointConnection.Properties.privateLinkServiceConnectionState.status
- CrossSubscriptionPE = $crossSubscriptionPE
- CrossTenantPE = $peXTenant
-
- Resource = $entry.ResourceName
- ResourceType = $entry.ResourceType
- ResourceId = $entry.ResourceId
- TargetSubresource = 'n/a'
- NICName = 'n/a'
- FQDN = 'n/a'
- ipAddresses = 'n/a'
- ResourceResourceGroup = $entry.ResourceResourceGroup
- ResourceSubscriptionName = $entry.ResourceSubscriptionName
- ResourceSubscriptionId = $entry.ResourceSubscriptionId
- ResourceMGPath = $entry.ResourceMGPath
- ResourceCrossTenant = 'false'
-
- Subnet = 'n/a'
- SubnetId = 'n/a'
- SubnetVNet = 'n/a'
- SubnetVNetId = 'n/a'
- SubnetVNetLocation = 'n/a'
- SubnetVNetResourceGroup = 'n/a'
- SubnetSubscriptionName = 'n/a'
- SubnetSubscription = 'n/a'
- SubnetMGPath = 'n/a'
- })
- }
- }
- }
-
- Write-Host " Processing Private Endpoints enrichment for $($arrayPrivateEndPoints.Count) Private Endpoint(s) where the Private Endpoint was returned from the PE API endpoint"
- $htVPrivateEndPoints = @{}
- foreach ($pe in $arrayPrivateEndPoints) {
- $htVPrivateEndPoints.($pe.id) = $pe
- }
-
- $htVPrivateEndPoints = @{}
- foreach ($pe in $arrayPrivateEndPoints) {
- $htVPrivateEndPoints.($pe.id) = $pe
- }
-
- foreach ($pe in $arrayPrivateEndPoints) {
-
- $peIdSplit = ($pe.id -split '/')
- $subscriptionId = $peIdSplit[2]
- $resourceGroup = $peIdSplit[4]
-
- $subscriptionName = 'n/a'
- $MGPath = 'n/a'
- if ($htSubscriptionsMgPath.($subscriptionId)) {
- $subHelper = $htSubscriptionsMgPath.($subscriptionId)
- $subscriptionName = $subHelper.displayName
- $MGPath = $subHelper.ParentNameChainDelimited
- }
-
- $SubnetSubscriptionName = 'n/a'
- $SubnetSubscription = 'n/a'
- $SubnetMGPath = 'n/a'
- $SubnetVNet = 'n/a'
- $SubnetVNetId = 'n/a'
- $SubnetVNetLocation = 'n/a'
- $SubnetVNetResourceGroup = 'n/a'
- if ($htSubnets.($pe.properties.subnet.id)) {
- $hlper = $htSubnets.($pe.properties.subnet.id)
- $SubnetSubscriptionName = $hlper.SubscriptionName
- $SubnetSubscription = $hlper.Subscription
- $SubnetMGPath = $hlper.MGPath
- $SubnetVNet = $hlper.VNet
- $SubnetVNetId = $hlper.VNetId
- $SubnetVNetLocation = $hlper.Location
- $SubnetVNetResourceGroup = $hlper.ResourceGroup
- }
-
- $resourceSplit = $false
- if ($pe.properties.privateLinkServiceConnections.Count -gt 0) {
- $resourceId = $pe.properties.privateLinkServiceConnections.properties.privateLinkServiceId
- $targetSubresource = $pe.properties.privateLinkServiceConnections.properties.groupIds -join ', '
- $resourceSplit = $pe.properties.privateLinkServiceConnections.properties.privateLinkServiceId -split '/'
- $peConnectionType = 'direct'
- $peConnectionState = $pe.properties.privateLinkServiceConnections.properties.privateLinkServiceConnectionState.status
- }
- if ($pe.properties.manualPrivateLinkServiceConnections.Count -gt 0) {
- $resourceId = $pe.properties.manualPrivateLinkServiceConnections.properties.privateLinkServiceId
- $targetSubresource = $pe.properties.manualPrivateLinkServiceConnections.properties.groupIds -join ', '
- $resourceSplit = $pe.properties.manualPrivateLinkServiceConnections.properties.privateLinkServiceId -split '/'
- $peConnectionType = 'manual'
- $peConnectionState = $pe.properties.manualPrivateLinkServiceConnections.properties.privateLinkServiceConnectionState.status
- }
-
- $resourceSubscriptionId = 'n/a'
- $resource = 'n/a'
- $resourceType = 'n/a'
- $resourceResourceGroup = 'n/a'
- $resourceSubscriptionName = 'n/a'
- $resourceMGPath = 'n/a'
- $crossSubscriptionPE = 'n/a'
- $resourceXTenant = 'unknown'
-
- if ($resourceSplit) {
- $ObjectGuid = [System.Guid]::empty
- if ([System.Guid]::TryParse($resourceSplit[2], [System.Management.Automation.PSReference]$ObjectGuid)) {
- $resourceSubscriptionId = $resourceSplit[2]
- $resource = $resourceSplit[8]
- $resourceType = "$($resourceSplit[6])/$($resourceSplit[7])"
- $resourceResourceGroup = $resourceSplit[4]
-
- if ($htSubscriptionsMgPath.($resourceSubscriptionId)) {
- $subHelper = $htSubscriptionsMgPath.($resourceSubscriptionId)
- $resourceSubscriptionName = $subHelper.displayName
- $resourceMGPath = $subHelper.ParentNameChainDelimited
- $resourceXTenant = $false
- }
- else {
- if ($htUnknownTenantsForSubscription.($resourceSubscriptionId)) {
- $remoteTenantId = $htUnknownTenantsForSubscription.($resourceSubscriptionId).TenantId
- $resourceMGPath = $remoteTenantId
- if ($remoteTenantId -eq $azApiCallConf['checkcontext'].tenant.id) {
- $resourceXTenant = $false
- }
- else {
- $resourceXTenant = $true
- }
- }
- else {
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($resourceSubscriptionId)?api-version=2020-01-01"
- $remoteTenantId = AzAPICall -AzAPICallConfiguration $azApiCallConf -uri $uri -listenOn 'content' -currentTask "getTenantId for subscriptionId '$($resourceSubscriptionId)'"
- $arrayRemoteMGPath = @()
- foreach ($remoteId in $remoteTenantId) {
- $objectGuid = [System.Guid]::empty
- if ([System.Guid]::TryParse($remoteId, [System.Management.Automation.PSReference]$ObjectGuid)) {
- if ($remoteId -in $MSTenantIds) {
- $arrayRemoteMGPath += "$remoteId (MS)"
- }
- else {
- $arrayRemoteMGPath += $remoteId
- }
- if ($remoteId -eq $azApiCallConf['checkcontext'].tenant.id) {
- $resourceXTenant = $false
- }
- else {
- $resourceXTenant = $true
- }
- }
- $script:htUnknownTenantsForSubscription.($resourceSubscriptionId) = @{}
- $script:htUnknownTenantsForSubscription.($resourceSubscriptionId).TenantId = $arrayRemoteMGPath -join ', '
- $resourceMGPath = $arrayRemoteMGPath -join ', '
- }
- }
- }
-
- if ($SubnetSubscription -eq $resourceSubscriptionId) {
- $crossSubscriptionPE = $false
- }
- else {
- $crossSubscriptionPE = $true
- }
-
- $crossTenantPE = $false
- if ($resourceXTenant -eq $true) {
- $crossTenantPE = $true
- }
-
- }
- }
-
- $null = $script:arrayPrivateEndpointsEnriched.Add([PSCustomObject]@{
- PEName = $pe.name
- PEId = $pe.id
- PELocation = $pe.location
- PEResourceGroup = $resourceGroup
- PESubscriptionName = $subscriptionName
- PESubscription = ($pe.id -split '/')[2]
- PEMGPath = $MGPath
- PEConnectionType = $peConnectionType
- PEConnectionState = $peConnectionState
- CrossSubscriptionPE = $crossSubscriptionPE
- CrossTenantPE = $crossTenantPE
-
- Resource = $resource
- ResourceType = $resourceType
- ResourceId = $resourceId
- TargetSubresource = $targetSubresource -join ', '
- NICName = $pe.properties.customNetworkInterfaceName
- FQDN = $pe.properties.customDnsConfigs.fqdn -join ', '
- ipAddresses = $pe.properties.customDnsConfigs.ipAddresses -join ', '
- ResourceResourceGroup = $resourceResourceGroup
- ResourceSubscriptionName = $resourceSubscriptionName
- ResourceSubscriptionId = $resourceSubscriptionId
- ResourceMGPath = $resourceMGPath
- ResourceCrossTenant = $resourceXTenant
-
- Subnet = $pe.properties.subnet.id -replace '.*/'
- SubnetId = $pe.properties.subnet.id
- SubnetVNet = $SubnetVNet
- SubnetVNetId = $SubnetVNetId
- SubnetVNetLocation = $SubnetVNetLocation
- SubnetVNetResourceGroup = $SubnetVNetResourceGroup
- SubnetSubscriptionName = $SubnetSubscriptionName
- SubnetSubscription = $SubnetSubscription
- SubnetMGPath = $SubnetMGPath
- })
- }
-
-
- $end = Get-Date
- Write-Host " Processing Private Endpoints enrichment duration: $((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds"
-}
-function processScopeInsightsMgOrSub($mgOrSub, $mgChild, $subscriptionId, $subscriptionsMgId) {
- $script:scopescnter++
- $htmlScopeInsights = $null
- $htmlScopeInsights = [System.Text.StringBuilder]::new()
- #region ScopeInsightsBaseCollection
- if ($mgOrSub -eq 'mg') {
- #$startScopeInsightsPreQueryMg = Get-Date
- #BLUEPRINT
- $blueprintReleatedQuery = $blueprintBaseQuery.where( { $_.MgId -eq $mgChild -and [String]::IsNullOrEmpty($_.SubscriptionId) -and [String]::IsNullOrEmpty($_.BlueprintAssignmentId) } )
- $blueprintsScoped = $blueprintReleatedQuery
- $blueprintsScopedCount = ($blueprintsScoped).count
- #Resources
- $mgAllChildSubscriptions = [System.Collections.ArrayList]@()
- $mgAllChildSubscriptions = foreach ($entry in $htSubscriptionsMgPath.keys) {
- if (($htSubscriptionsMgPath.($entry).ParentNameChain) -contains $mgchild) {
- $entry
- }
- }
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- $resourcesAllChildSubscriptions = [System.Collections.ArrayList]@()
- foreach ($mgAllChildSubscription in $mgAllChildSubscriptions) {
- foreach ($resource in ($resourcesAllGroupedBySubcriptionId.where( { $_.name -eq $mgAllChildSubscription } )).group | Sort-Object -Property type, location) {
- $null = $resourcesAllChildSubscriptions.Add($resource)
- }
-
- }
- $resourcesAllChildSubscriptionsArray = [System.Collections.ArrayList]@()
- $grp = $resourcesAllChildSubscriptions | Group-Object -Property type, location
- foreach ($resLoc in $grp) {
- $cnt = 0
- $ResoureTypeAndLocation = $resLoc.Name.split(',')
- $resLoc.Group.count_ | ForEach-Object { $cnt += $_ }
- $null = $resourcesAllChildSubscriptionsArray.Add([PSCustomObject]@{
- ResourceType = $ResoureTypeAndLocation[0]
- Location = $ResoureTypeAndLocation[1]
- ResourceCount = $cnt
- })
- }
- $resourcesAllChildSubscriptions.count_ | ForEach-Object { $resourcesAllChildSubscriptionTotal += $_ }
- $resourcesAllChildSubscriptionResourceTypeCount = (($resourcesAllChildSubscriptions | Sort-Object -Property type -Unique) | Measure-Object).count
- $resourcesAllChildSubscriptionLocationCount = (($resourcesAllChildSubscriptions | Sort-Object -Property location -Unique) | Measure-Object).count
- }
- #childrenMgInfo
- $mgAllChildMgs = [System.Collections.ArrayList]@()
- $mgAllChildMgs = foreach ($entry in $htManagementGroupsMgPath.keys) {
- if (($htManagementGroupsMgPath.($entry).path) -contains $mgchild) {
- $entry
- }
- }
-
- $arrayPolicyAssignmentsEnrichedForThisManagementGroup = ($arrayPolicyAssignmentsEnrichedGroupedByManagementGroup.where( { $_.name -eq $mgChild } )).group
- $arrayPolicyAssignmentsEnrichedForThisManagementGroupGroupedByPolicyVariant = $arrayPolicyAssignmentsEnrichedForThisManagementGroup | Group-Object -Property PolicyVariant
- $arrayPolicyAssignmentsEnrichedForThisManagementGroupVariantPolicy = ($arrayPolicyAssignmentsEnrichedForThisManagementGroupGroupedByPolicyVariant.where( { $_.name -eq 'Policy' } )).group
- $arrayPolicyAssignmentsEnrichedForThisManagementGroupVariantPolicySet = ($arrayPolicyAssignmentsEnrichedForThisManagementGroupGroupedByPolicyVariant.where( { $_.name -eq 'PolicySet' } )).group
-
- if ($azAPICallConf['htParameters'].NoMDfCSecureScore -eq $false) {
- if ([string]::IsNullOrEmpty(($htMgASCSecureScore).($mgChild).SecureScore) -or [string]::IsNullOrWhiteSpace(($htMgASCSecureScore).($mgChild).SecureScore)) {
- $managementGroupASCPoints = 'n/a'
- }
- else {
- $managementGroupASCPoints = ($htMgASCSecureScore).($mgChild).SecureScore
- }
- }
- else {
- $managementGroupASCPoints = "excluded (-NoMDfCSecureScore $($azAPICallConf['htParameters'].NoMDfCSecureScore))"
- }
-
- $cssClass = 'mgDetailsTable'
-
- #$endScopeInsightsPreQueryMg = Get-Date
- #Write-Host " ScopeInsights MG PreQuery processing duration: $((NEW-TIMESPAN -Start $startScopeInsightsPreQueryMg -End $endScopeInsightsPreQueryMg).TotalSeconds) seconds"
- }
- if ($mgOrSub -eq 'sub') {
- #$startScopeInsightsPreQuerySub = Get-Date
- #BLUEPRINT
- $blueprintReleatedQuery = $blueprintBaseQuery.where( { $_.SubscriptionId -eq $subscriptionId -and -not [String]::IsNullOrEmpty($_.BlueprintName) } )
- $blueprintsAssigned = $blueprintReleatedQuery.where( { -not [String]::IsNullOrEmpty($_.BlueprintAssignmentId) } )
- $blueprintsAssignedCount = ($blueprintsAssigned).count
- $blueprintsScoped = $blueprintReleatedQuery.where( { $_.BlueprintScoped -eq "/subscriptions/$subscriptionId" -and [String]::IsNullOrEmpty($_.BlueprintAssignmentId) } )
- $blueprintsScopedCount = ($blueprintsScoped).count
- #SubscriptionDetails
- $subPath = $htSubscriptionsMgPath.($subscriptionId).pathDelimited
- $subscriptionDetailsReleatedQuery = $htSubDetails.($subscriptionId).details
- $subscriptionState = ($subscriptionDetailsReleatedQuery).SubscriptionState
- $subscriptionQuotaId = ($subscriptionDetailsReleatedQuery).SubscriptionQuotaId
- $subscriptionResourceGroupsCount = ($resourceGroupsAll.where( { $_.subscriptionId -eq $subscriptionId } )).count_
- if (-not $subscriptionResourceGroupsCount) {
- $subscriptionResourceGroupsCount = 0
- }
- $subscriptionASCPoints = ($subscriptionDetailsReleatedQuery).SubscriptionASCSecureScore
-
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- #Resources
- $resourcesSubscription = [System.Collections.ArrayList]@()
- foreach ($resource in ($resourcesAllGroupedBySubcriptionId.where( { $_.name -eq $subscriptionId } )).group | Sort-Object -Property type, location) {
- $null = $resourcesSubscription.Add($resource)
- }
-
- $resourcesSubscriptionTotal = 0
- $resourcesSubscription.count_ | ForEach-Object { $resourcesSubscriptionTotal += $_ }
- $resourcesSubscriptionResourceTypeCount = (($resourcesSubscription | Sort-Object -Property type -Unique)).count
- $resourcesSubscriptionLocationCount = (($resourcesSubscription | Sort-Object -Property location -Unique)).count
- }
-
- $arrayPolicyAssignmentsEnrichedForThisSubscription = ($arrayPolicyAssignmentsEnrichedGroupedBySubscription.where( { $_.name -eq $subscriptionId } )).group
- $arrayPolicyAssignmentsEnrichedForThisSubscriptionGroupedByPolicyVariant = $arrayPolicyAssignmentsEnrichedForThisSubscription | Group-Object -Property PolicyVariant
- $arrayPolicyAssignmentsEnrichedForThisSubscriptionVariantPolicy = ($arrayPolicyAssignmentsEnrichedForThisSubscriptionGroupedByPolicyVariant.where( { $_.name -eq 'Policy' } )).group
- $arrayPolicyAssignmentsEnrichedForThisSubscriptionVariantPolicySet = ($arrayPolicyAssignmentsEnrichedForThisSubscriptionGroupedByPolicyVariant.where( { $_.name -eq 'PolicySet' } )).group
-
- $arrayDefenderPlansSubscription = $defenderPlansGroupedBySub.where( { $_.Name -like "*$($subscriptionId)*" } )
-
- $arrayUserAssignedIdentities4ResourcesSubscription = $arrayUserAssignedIdentities4Resources.where( { $_.resourceSubscriptionId -eq $subscriptionId -or $_.miSubscriptionId -eq $subscriptionId } )
- $arrayUserAssignedIdentities4ResourcesSubscriptionCount = $arrayUserAssignedIdentities4ResourcesSubscription.Count
-
- if ($subFeaturesGroupedBySubscription) {
- $subscriptionFeatures = $subFeaturesGroupedBySubscription.where({ $_.name -eq $subscriptionId })
- }
-
- $cssClass = 'subDetailsTable'
-
- #$endScopeInsightsPreQuerySub = Get-Date
- #Write-Host " ScopeInsights SUB PreQuery processing duration: $((NEW-TIMESPAN -Start $startScopeInsightsPreQuerySub -End $endScopeInsightsPreQuerySub).TotalSeconds) seconds"
- }
- #endregion ScopeInsightsBaseCollection
-
- if ($mgOrSub -eq 'sub') {
-
- if ($htDefenderEmailContacts.($subscriptionDetailsReleatedQuery.subscriptionId)) {
- $hlpDefenderEmailContacts = $htDefenderEmailContacts.($subscriptionDetailsReleatedQuery.subscriptionId)
- $MDfCEmailNotificationsState = $hlpDefenderEmailContacts.alertNotificationsState
- $MDfCEmailNotificationsSeverity = $hlpDefenderEmailContacts.alertNotificationsminimalSeverity
- $MDfCEmailNotificationsRoles = $hlpDefenderEmailContacts.roles
- $MDfCEmailNotificationsEmails = $hlpDefenderEmailContacts.emails
- }
- else {
- $MDfCEmailNotificationsState = ''
- $MDfCEmailNotificationsSeverity = ''
- $MDfCEmailNotificationsRoles = ''
- $MDfCEmailNotificationsEmails = ''
- }
-
- [void]$htmlScopeInsights.AppendLine(@"
-Subscription Name: $($subscriptionDetailsReleatedQuery.subscription -replace '<', '<' -replace '>', '>')
-Subscription Id: $($subscriptionDetailsReleatedQuery.subscriptionId)
-Subscription Path: $subPath
-State: $subscriptionState
-QuotaId: $subscriptionQuotaId
- Microsoft Defender for Cloud Secure Score: $subscriptionASCPoints Video , Blog , docs
- Microsoft Defender for Cloud 'Email notifications' state: $MDfCEmailNotificationsState
- Microsoft Defender for Cloud 'Email notifications' severity: $MDfCEmailNotificationsSeverity
- Microsoft Defender for Cloud 'Email notifications' roles: $MDfCEmailNotificationsRoles
- Microsoft Defender for Cloud 'Email notifications' emails: $MDfCEmailNotificationsEmails
-
-"@)
-
- #region ScopeInsightsDefenderPlans
- if ($arrayDefenderPlansSubscription) {
-
- $defenderPlanSubscriptionDeprecatedContainerRegistry = $false
- $defenderPlanSubscriptionDeprecatedKubernetesService = $false
-
- $containerRegistryStandardCount = ($arrayDefenderPlansSubscription.Group.where( { $_.defenderPlan -eq 'ContainerRegistry' -and $_.defenderPlanTier -eq 'Standard' } )).Count
- $kubernetesServiceStandardCount = ($arrayDefenderPlansSubscription.Group.where( { $_.defenderPlan -eq 'KubernetesService' -and $_.defenderPlanTier -eq 'Standard' } )).Count
- if ($containerRegistryStandardCount -gt 0) {
- $defenderPlanSubscriptionDeprecatedContainerRegistry = $true
- }
- if ($kubernetesServiceStandardCount -gt 0) {
- $defenderPlanSubscriptionDeprecatedKubernetesService = $true
- }
-
- $defenderCapabilitiesSubscription = ($arrayDefenderPlansSubscription.group.defenderPlan | Sort-Object -Unique)
- $tfCount = 1
- $htmlTableId = "ScopeInsights_DefenderPlans_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- Microsoft Defender for Cloud plans docs
-
-"@)
-
- if ($defenderPlanSubscriptionDeprecatedContainerRegistry) {
- [void]$htmlScopeInsights.AppendLine(@'
-
Using deprecated plan 'Container registries'
docs
-'@)
- }
- if ($defenderPlanSubscriptionDeprecatedKubernetesService) {
- [void]$htmlScopeInsights.AppendLine(@'
-
Using deprecated plan 'Kubernetes'
docs
-'@)
- }
-
- [void]$htmlScopeInsights.AppendLine(@"
-
Download CSV
semicolon |
comma
-
-
-
-Plan
-Tier
-
-
-
-
-"@)
-
- foreach ($plan in $arrayDefenderPlansSubscription.Group | Sort-Object -Property defenderPlan) {
- if (($plan.defenderPlan -eq 'ContainerRegistry' -and $plan.defenderPlanTier -eq 'Standard') -or ($plan.defenderPlan -eq 'KubernetesService' -and $plan.defenderPlanTier -eq 'Standard')) {
- $thisDefenderPlan = " $($plan.defenderPlan)"
- }
- else {
- $thisDefenderPlan = $plan.defenderPlan
- }
- [void]$htmlScopeInsights.AppendLine(@"
-
- $($thisDefenderPlan)
- $($plan.defenderPlanTier)
-
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-
-"@)
- }
- else {
- $subscriptionSkippedMDfC = $arrayDefenderPlansSubscriptionsSkipped.where( { $_.subscriptionId -eq $subscriptionId } )
- if ($subscriptionSkippedMDfC.Count -gt 0) {
- if ($subscriptionSkippedMDfC.reason -eq 'SubScriptionNotRegistered') {
- [void]$htmlScopeInsights.AppendLine(@"
- Microsoft Defender for Cloud plans - Subscription skipped ($($subscriptionSkippedMDfC.reason)) (ResourceProvider: Microsoft.Security) docs
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- Microsoft Defender for Cloud plans - Subscription skipped ($($subscriptionSkippedMDfC.reason))
-"@)
- }
-
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No Microsoft Defender for Cloud plans docs
-'@)
- }
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsDefenderPlans
-
- #region ScopeInsightsDiganosticsSubscription
- if (($htDiagnosticSettingsMgSub).sub.($subscriptionId)) {
- $diagnosticsSubCount = (($htDiagnosticSettingsMgSub).sub.($subscriptionId).Values.Count)
- $tfCount = $diagnosticsSubCount
- $htmlTableId = "ScopeInsights_DiagnosticsSub_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- $diagnosticsSubCount Subscription Diagnostic settings
-
-
Download CSV
semicolon |
comma
-
-
-
-Diagnostic setting
-Target
-Target Id
-"@)
- foreach ($logCategory in $diagnosticSettingsSubCategories) {
- [void]$htmlScopeInsights.AppendLine(@"
-$logCategory
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-
-'@)
- $htmlScopeInsightsDiagnosticsSub = $null
- $htmlScopeInsightsDiagnosticsSub = foreach ($entry in ($htDiagnosticSettingsMgSub).sub.($subscriptionId).keys | Sort-Object) {
- foreach ($diagset in ($htDiagnosticSettingsMgSub).sub.($subscriptionId).$entry.keys | Sort-Object) {
- @"
-
-$(($htDiagnosticSettingsMgSub).sub.($subscriptionId).$entry.$diagset.DiagnosticSettingName)
-$(($htDiagnosticSettingsMgSub).sub.($subscriptionId).$entry.$diagset.DiagnosticTargetType)
-$(($htDiagnosticSettingsMgSub).sub.($subscriptionId).$entry.$diagset.DiagnosticTargetId)
-"@
- foreach ($logCategory in $diagnosticSettingsSubCategories) {
- if (($htDiagnosticSettingsMgSub).sub.($subscriptionId).$entry.$diagset.DiagnosticCategoriesHt.($logCategory)) {
- @"
-$(($htDiagnosticSettingsMgSub).sub.($subscriptionId).$entry.$diagset.DiagnosticCategoriesHt.($logCategory))
-"@
- }
- else {
- @'
-n/a
-'@
- }
- }
- @'
-
-'@
- }
- }
-
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsDiagnosticsSub)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No Subscription Diagnostic settings docs
-'@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsDiganosticsSubscription
-
- #Tags
- #region ScopeInsightsTags
- $tagsSubscriptionCount = ($htSubscriptionTags.$subscriptionId.Keys).count
- if ($tagsSubscriptionCount -gt 0) {
- $tfCount = $tagsSubscriptionCount
- $htmlTableId = "ScopeInsights_Tags_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
-
- $tagsSubscriptionCount Subscription Tags | Limit: ($tagsSubscriptionCount/$LimitTagsSubscription)
-
-
Download CSV
semicolon |
comma
-
-
-
-Tag Name
-Tag Value
-
-
-
-"@)
- $htmlScopeInsightsTags = $null
- $htmlScopeInsightsTags = foreach ($tag in (($htSubscriptionTags).($subscriptionId)).keys | Sort-Object) {
- @"
-
-$tag
-$($htSubscriptionTags.$subscriptionId[$tag])
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsTags)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $tagsSubscriptionCount Subscription Tags
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsTags
-
- #TagNameUsage
- #region ScopeInsightsTagNameUsage
- $arrayTagListSubscription = [System.Collections.ArrayList]@()
- foreach ($tagScope in $htSubscriptionTagList.($subscriptionId).keys) {
- foreach ($tagScopeTagName in $htSubscriptionTagList.($subscriptionId).$tagScope.keys) {
- $null = $arrayTagListSubscription.Add([PSCustomObject]@{
- Scope = $tagScope
- TagName = ($tagScopeTagName)
- TagCount = $htSubscriptionTagList.($subscriptionId).($tagScope).($tagScopeTagName)
- })
- }
- }
- $tagsUsageCount = ($arrayTagListSubscription).Count
-
- if ($tagsUsageCount -gt 0) {
- $tagNamesUniqueCount = ($arrayTagListSubscription | Sort-Object -Property TagName -Unique).Count
- $tagNamesUsedInScopes = ($arrayTagListSubscription | Sort-Object -Property Scope -Unique).scope -join "$($CsvDelimiterOpposite) "
- $tfCount = $tagsUsageCount
- $htmlTableId = "ScopeInsights_TagNameUsage_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
-
- Tag Name Usage ($tagNamesUniqueCount unique Tag Names applied at $($tagNamesUsedInScopes)
-
-
Resource naming and tagging decision guide docs
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-TagName
-Count
-
-
-
-"@)
- $htmlScopeInsightsTagsUsage = $null
- $htmlScopeInsightsTagsUsage = foreach ($tagEntry in $arrayTagListSubscription | Sort-Object Scope, TagName -CaseSensitive) {
- @"
-
-$($tagEntry.Scope)
-$($tagEntry.TagName)
-$($tagEntry.TagCount)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsTagsUsage)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- Tag Name Usage ($tagsUsageCount Tags) docs
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsTagNameUsage
-
- #Consumption
- #$startScopeInsightsConsumptionSub = Get-Date
- #region ScopeInsightsConsumptionSub
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
-
- if ($htAzureConsumptionSubscriptions.($subscriptionId).ConsumptionData) {
- $consumptionData = $htAzureConsumptionSubscriptions.($subscriptionId).ConsumptionData
-
- $arrayTotalCostSummarySub = @()
- $arrayConsumptionData = [System.Collections.ArrayList]@()
-
- $totalCost = 0
-
- $currency = $htAzureConsumptionSubscriptions.($subscriptionId).Currency
- $consumedServiceCount = ($consumptionData.ResourceType | Sort-Object -Unique | Measure-Object).Count
- $resourceCount = ($consumptionData.ResourceId | Sort-Object -Unique | Measure-Object).Count
- $subConsumptionDataGrouped = $consumptionData | Group-Object -Property ResourceType, ChargeType, MeterCategory
-
- foreach ($consumptionline in $subConsumptionDataGrouped) {
-
- $costConsumptionLine = ($consumptionline.group.PreTaxCost | Measure-Object -Sum).Sum
- if ([math]::Round($costConsumptionLine, 2) -eq 0) {
- $cost = $costConsumptionLine.ToString('0.0000')
- }
- else {
- $cost = [math]::Round($costConsumptionLine, 2).ToString('0.00')
- }
-
- $null = $arrayConsumptionData.Add([PSCustomObject]@{
- ResourceType = ($consumptionline.name).split(', ')[0]
- ConsumedServiceChargeType = ($consumptionline.name).split(', ')[1]
- ConsumedServiceCategory = ($consumptionline.name).split(', ')[2]
- ConsumedServiceInstanceCount = $consumptionline.Count
- ConsumedServiceCost = $cost #[decimal]$cost
- ConsumedServiceCurrency = $currency
- })
-
- $totalCost = $htAzureConsumptionSubscriptions.($subscriptionId).TotalCost
-
- }
- if ([math]::Round($totalCost, 2) -eq 0) {
- $totalCost = $totalCost
- }
- else {
- $totalCost = [math]::Round($totalCost, 2).ToString('0.00')
- }
- $arrayTotalCostSummarySub += "$($totalCost) $($currency) generated by $($resourceCount) Resources ($($consumedServiceCount) ResourceTypes)"
-
- $tfCount = ($arrayConsumptionData | Measure-Object).Count
- $htmlTableId = "ScopeInsights_Consumption_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- Total cost $($arrayTotalCostSummarySub -join ', ') last $AzureConsumptionPeriod days ($azureConsumptionStartDate - $azureConsumptionEndDate)
-
-
Download CSV
semicolon |
comma
-
-
-
-ChargeType
-ResourceType
-Category
-ResourceCount
-Cost ($($AzureConsumptionPeriod)d)
-Currency
-
-
-
-"@)
- $htmlScopeInsightsConsumptionSub = $null
- $htmlScopeInsightsConsumptionSub = foreach ($consumptionLine in $arrayConsumptionData) {
- @"
-
-$($consumptionLine.ConsumedServiceChargeType)
-$($consumptionLine.ResourceType)
-$($consumptionLine.ConsumedServiceCategory)
-$($consumptionLine.ConsumedServiceInstanceCount)
-$($consumptionLine.ConsumedServiceCost)
-$($currency)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsConsumptionSub)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No Consumption data available
-'@)
- }
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No Consumption data available as switch parameter -DoAzureConsumption was not applied
-'@)
- }
-
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsConsumptionSub
- #$endScopeInsightsConsumptionSub = Get-Date
- #Write-Host " **ScopeInsightsConsumptionSub data duration: $((NEW-TIMESPAN -Start $startScopeInsightsConsumptionSub -End $endScopeInsightsConsumptionSub).TotalSeconds) seconds"
-
- #ResourceGroups
- #region ScopeInsightsResourceGroups
- if ($subscriptionResourceGroupsCount -gt 0) {
- [void]$htmlScopeInsights.AppendLine(@"
- $subscriptionResourceGroupsCount Resource Groups | Limit: ($subscriptionResourceGroupsCount/$LimitResourceGroups)
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $subscriptionResourceGroupsCount Resource Groups
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsResourceGroups
-
- #ResourceProvider
- #region ScopeInsightsResourceProvidersDetailed
- if ($azAPICallConf['htParameters'].NoResourceProvidersAtAll -eq $false) {
- if ($azAPICallConf['htParameters'].NoResourceProvidersDetailed -eq $false) {
- if (($htResourceProvidersAll).($subscriptionId)) {
- $tfCount = ($htResourceProvidersAll).($subscriptionId).Providers.Count
- $htmlTableId = "ScopeInsights_ResourceProvider_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- Resource Providers Detailed
-
-
Download CSV
semicolon |
comma
-
-
-
-Provider
-State
-
-
-
-"@)
- $htmlScopeInsightsResourceProvidersDetailed = $null
- $htmlScopeInsightsResourceProvidersDetailed = foreach ($provider in ($htResourceProvidersAll).($subscriptionId).Providers) {
- @"
-
-$($provider.namespace)
-$($provider.registrationState)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsResourceProvidersDetailed)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $(($htResourceProvidersAll.Keys).count) Resource Providers
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
- }
- #endregion ScopeInsightsResourceProvidersDetailed
-
- #region ScopeInsightsSubscriptionFeatures
- if ($subscriptionFeatures) {
- $subscriptionFeaturesCount = $subscriptionFeatures.Group.Count
-
- $tfCount = $subscriptionFeaturesCount
- $htmlTableId = "ScopeInsights_SubscriptionFeatures_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
-
- [void]$htmlScopeInsights.AppendLine(@"
-
- $tfCount enabled Subscription Features
-
-
Set up preview features in Azure subscription docs
-
-
-
-Feature
-
-
-
-"@)
-
- foreach ($feature in $subscriptionFeatures.Group | Sort-Object -Property feature) {
- [void]$htmlScopeInsights.AppendLine(@"
- $($feature.feature)
-"@)
- }
-
-
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- 0 enabled Subscription Features docs
-'@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsSubscriptionFeatures
-
- #ResourceLocks
- #region ScopeInsightsResourceLocks
- if ($htResourceLocks.($subscriptionId)) {
- $tfCount = 6
- $htmlTableId = "ScopeInsights_ResourceLocks_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
-
- $subscriptionLocksCannotDeleteCount = $htResourceLocks.($subscriptionId).SubscriptionLocksCannotDeleteCount
- $subscriptionLocksReadOnlyCount = $htResourceLocks.($subscriptionId).SubscriptionLocksReadOnlyCount
- $resourceGroupsLocksCannotDeleteCount = $htResourceLocks.($subscriptionId).ResourceGroupsLocksCannotDeleteCount
- $resourceGroupsLocksReadOnlyCount = $htResourceLocks.($subscriptionId).ResourceGroupsLocksReadOnlyCount
- $resourcesLocksCannotDeleteCount = $htResourceLocks.($subscriptionId).ResourcesLocksCannotDeleteCount
- $resourcesLocksReadOnlyCount = $htResourceLocks.($subscriptionId).ResourcesLocksReadOnlyCount
-
- [void]$htmlScopeInsights.AppendLine(@"
-
- Resource Locks
-
-
Considerations before applying locks docs
-
-
-
-Lock scope
-Lock type
-presence
-
-
-
-Subscription CannotDelete $($subscriptionLocksCannotDeleteCount)
-Subscription ReadOnly $($subscriptionLocksReadOnlyCount)
-ResourceGroup CannotDelete $($resourceGroupsLocksCannotDeleteCount)
-ResourceGroup ReadOnly $($resourceGroupsLocksReadOnlyCount)
-Resource CannotDelete $($resourcesLocksCannotDeleteCount)
-Resource ReadOnly $($resourcesLocksReadOnlyCount)
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- 0 Resource Locks docs
-'@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsResourceLocks
-
- }
-
- #MgChildInfo
- #region ScopeInsightsManagementGroups
- if ($mgOrSub -eq 'mg') {
-
- [void]$htmlScopeInsights.AppendLine(@"
- $(($mgAllChildMgs).count -1) ManagementGroups below this scope
-$(($mgAllChildSubscriptions).count) Subscriptions below this scope
- Microsoft Defender for Cloud Secure Score: $managementGroupASCPoints Video , Blog , docs
-
-"@)
-
- #region ScopeInsightsDiagnosticsMg
- if (($htDiagnosticSettingsMgSub).mg.($mgChild)) {
- $diagnosticsMgCount = (($htDiagnosticSettingsMgSub).mg.($mgChild).Values.Count)
- $tfCount = $diagnosticsMgCount
- $htmlTableId = "ScopeInsights_DiagnosticsMg_$($mgChild -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- $diagnosticsMgCount Management Group Diagnostic settings
-
-
Download CSV
semicolon |
comma
-
-
-
-Diagnostic setting
-Target
-Target Id
-"@)
- foreach ($logCategory in $diagnosticSettingsMgCategories) {
- [void]$htmlScopeInsights.AppendLine(@"
-$logCategory
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-
-'@)
- $htmlScopeInsightsDiagnosticsMg = $null
- $htmlScopeInsightsDiagnosticsMg = foreach ($entry in ($htDiagnosticSettingsMgSub).mg.($mgChild).keys | Sort-Object) {
- foreach ($diagset in ($htDiagnosticSettingsMgSub).mg.($mgChild).$entry.keys | Sort-Object) {
- @"
-
-$(($htDiagnosticSettingsMgSub).mg.($mgChild).$entry.$diagset.DiagnosticSettingName)
-$(($htDiagnosticSettingsMgSub).mg.($mgChild).$entry.$diagset.DiagnosticTargetType)
-$(($htDiagnosticSettingsMgSub).mg.($mgChild).$entry.$diagset.DiagnosticTargetId)
-"@
- foreach ($logCategory in $diagnosticSettingsMgCategories) {
- if (($htDiagnosticSettingsMgSub).mg.($mgChild).$entry.$diagset.DiagnosticCategoriesHt.($logCategory)) {
- @"
-$(($htDiagnosticSettingsMgSub).mg.($mgChild).$entry.$diagset.DiagnosticCategoriesHt.($logCategory))
-"@
- }
- else {
- @'
-n/a
-'@
- }
- }
- @'
-
-'@
- }
- }
-
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsDiagnosticsMg)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No Management Group Diagnostic settings docs
-'@)
- }
- #endregion ScopeInsightsDiagnosticsMg
-
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
-
- #$startScopeInsightsConsumptionMg = Get-Date
- #region ScopeInsightsConsumptionMg
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- if ($allConsumptionDataCount -gt 0) {
-
- $consumptionData = $htManagementGroupsCost.($mgchild).consumptionDataSubscriptions
- if (($consumptionData | Measure-Object).Count -gt 0) {
- $arrayTotalCostSummaryMg = @()
- $arrayConsumptionData = [System.Collections.ArrayList]@()
- $consumptionDataGroupedByCurrency = $consumptionData | Group-Object -Property Currency
- foreach ($currency in $consumptionDataGroupedByCurrency) {
- $totalCost = 0
- $tenantSummaryConsumptionDataGrouped = $currency.group | Group-Object -Property ResourceType, ChargeType, MeterCategory
- $subsCount = ($tenantSummaryConsumptionDataGrouped.group.subscriptionId | Sort-Object -Unique).Count
- $consumedServiceCount = ($tenantSummaryConsumptionDataGrouped.group.ResourceType | Sort-Object -Unique).Count
- $resourceCount = ($tenantSummaryConsumptionDataGrouped.group.ResourceId | Sort-Object -Unique).Count
- foreach ($consumptionline in $tenantSummaryConsumptionDataGrouped) {
-
- $costConsumptionLine = ($consumptionline.group.PreTaxCost | Measure-Object -Sum).Sum
- if ([math]::Round($costConsumptionLine, 2) -eq 0) {
- $cost = $costConsumptionLine.ToString('0.0000')
- }
- else {
- $cost = [math]::Round($costConsumptionLine, 2).ToString('0.00')
- }
-
- $null = $arrayConsumptionData.Add([PSCustomObject]@{
- ResourceType = ($consumptionline.name).split(', ')[0]
- ConsumedServiceChargeType = ($consumptionline.name).split(', ')[1]
- ConsumedServiceCategory = ($consumptionline.name).split(', ')[2]
- ConsumedServiceInstanceCount = $consumptionline.Count
- ConsumedServiceCost = $cost #[decimal]$cost
- ConsumedServiceSubscriptions = ($consumptionline.group.SubscriptionId | Sort-Object -Unique).Count
- ConsumedServiceCurrency = $currency.Name
- })
-
- $totalCost = $totalCost + $costConsumptionLine
- }
- if ([math]::Round($totalCost, 2) -eq 0) {
- $totalCost = $totalCost.ToString('0.0000')
- }
- else {
- $totalCost = [math]::Round($totalCost, 2).ToString('0.00')
- }
- $arrayTotalCostSummaryMg += "$($totalCost) $($currency.Name) generated by $($resourceCount) Resources ($($consumedServiceCount) ResourceTypes) in $($subsCount) Subscriptions"
- }
-
- $tfCount = ($arrayConsumptionData).Count
- $htmlTableId = "ScopeInsights_Consumption_$($mgChild -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- Total cost $($arrayTotalCostSummaryMg -join "$CsvDelimiterOpposite ") last $AzureConsumptionPeriod days ($azureConsumptionStartDate - $azureConsumptionEndDate)
-
-
Download CSV
-
semicolon |
-
comma
-
-
-
-ChargeType
-ResourceType
-Category
-ResourceCount
-Cost ($($AzureConsumptionPeriod)d)
-Currency
-Subscriptions
-
-
-
-"@)
- $htmlScopeInsightsConsumptionMg = $null
- $htmlScopeInsightsConsumptionMg = foreach ($consumptionLine in $arrayConsumptionData) {
- @"
-
-$($consumptionLine.ConsumedServiceChargeType)
-$($consumptionLine.ResourceType)
-$($consumptionLine.ConsumedServiceCategory)
-$($consumptionLine.ConsumedServiceInstanceCount)
-$($consumptionLine.ConsumedServiceCost)
-$($consumptionLine.ConsumedServiceCurrency)
-$($consumptionLine.ConsumedServiceSubscriptions)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsConsumptionMg)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No Consumption data available for Subscriptions under this ManagementGroup
-'@)
- }
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No Consumption data available
-'@)
- }
-
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No Consumption data available as switch parameter -DoAzureConsumption was not applied
-'@)
- }
- #endregion ScopeInsightsConsumptionMg
- #$endScopeInsightsConsumptionMg = Get-Date
- #Write-Host " ++ScopeInsightsConsumptionMg duration: ($((NEW-TIMESPAN -Start $startScopeInsightsConsumptionMg -End $endScopeInsightsConsumptionMg).TotalSeconds) seconds)"
-
-
- }
- #endregion ScopeInsightsManagementGroups
-
- #ScopeInsightsResources
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- #resources
- #region ScopeInsightsResources
- if ($mgOrSub -eq 'mg') {
- if ($resourcesAllChildSubscriptionLocationCount -gt 0) {
- $tfCount = ($resourcesAllChildSubscriptionsArray).count
- $htmlTableId = "ScopeInsights_Resources_$($mgChild -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- $resourcesAllChildSubscriptionResourceTypeCount ResourceTypes ($resourcesAllChildSubscriptionTotal Resources) in $resourcesAllChildSubscriptionLocationCount Locations (all Subscriptions below this scope)
-
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Location
-Count
-
-
-
-"@)
- $htmlScopeInsightsResources = $null
- $htmlScopeInsightsResources = foreach ($resourceAllChildSubscriptionResourceTypePerLocation in $resourcesAllChildSubscriptionsArray | Sort-Object @{Expression = { $_.ResourceType } }, @{Expression = { $_.location } }) {
- @"
-
-$($resourceAllChildSubscriptionResourceTypePerLocation.ResourceType)
-$($resourceAllChildSubscriptionResourceTypePerLocation.location)
-$($resourceAllChildSubscriptionResourceTypePerLocation.ResourceCount)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsResources)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $resourcesAllChildSubscriptionResourceTypeCount ResourceTypes (all Subscriptions below this scope)
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
-
- if ($mgOrSub -eq 'sub') {
- if ($resourcesSubscriptionResourceTypeCount -gt 0) {
- $tfCount = ($resourcesSubscription).Count
- $htmlTableId = "ScopeInsights_Resources_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- $resourcesSubscriptionResourceTypeCount ResourceTypes ($resourcesSubscriptionTotal Resources) in $resourcesSubscriptionLocationCount Locations
-
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Location
-Count
-
-
-
-"@)
- $htmlScopeInsightsResources = $null
- $htmlScopeInsightsResources = foreach ($resourceSubscriptionResourceTypePerLocation in $resourcesSubscription | Sort-Object @{Expression = { $_.type } }, @{Expression = { $_.location } }, @{Expression = { $_.count_ } }) {
- @"
-
-$($resourceSubscriptionResourceTypePerLocation.type)
-$($resourceSubscriptionResourceTypePerLocation.location)
-$($resourceSubscriptionResourceTypePerLocation.count_)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsResources)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $resourcesSubscriptionResourceTypeCount ResourceTypes
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
- #endregion ScopeInsightsResources
- }
-
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- #region ScopeInsightsCAFResourceNamingALL
- if ($mgOrSub -eq 'sub') {
- $resourcesIdsAllCAFNamingRelevantThisSubscription = $resourcesIdsAllCAFNamingRelevantGroupedBySubscription.where({ $_.Name -eq $subscriptionId })
- if ($resourcesIdsAllCAFNamingRelevantThisSubscription) {
- $resourcesIdsAllCAFNamingRelevantThisSubscriptionGroupedByType = $resourcesIdsAllCAFNamingRelevantThisSubscription.Group | Group-Object -Property type
- $resourcesIdsAllCAFNamingRelevantThisSubscriptionGroupedByTypeCount = ($resourcesIdsAllCAFNamingRelevantThisSubscriptionGroupedByType | Measure-Object).Count
-
- $tfCount = $resourcesIdsAllCAFNamingRelevantThisSubscriptionGroupedByTypeCount
- $htmlTableId = "ScopeInsights_CAFResourceNamingALL_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- CAF Naming Recommendation Compliance
-
-
CAF - Recommended abbreviations for Azure resource types docs
-
Resource details can be found in the CSV output *_ResourcesAll.csv
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Recommendation
-ResourceFriendlyName
-passed
-failed
-passed percentage
-
-
-
-"@)
- $htmlScopeInsightsCAFResourceNamingALL = $null
- $htmlScopeInsightsCAFResourceNamingALL = foreach ($entry in $resourcesIdsAllCAFNamingRelevantThisSubscriptionGroupedByType) {
-
- $resourceTypeGroupedByCAFResourceNamingResult = $entry.Group | Group-Object -Property cafResourceNamingResult, cafResourceNaming
- if ($entry.Group.cafResourceNaming.Count -gt 1) {
- $namingConvention = ($entry.Group.cafResourceNaming)[0]
- $namingConventionFriendlyName = ($entry.Group.cafResourceNamingFriendlyName)[0]
- }
- else {
- $namingConvention = $entry.Group.cafResourceNaming
- $namingConventionFriendlyName = $entry.Group.cafResourceNamingFriendlyName
- }
-
- $passed = 0
- $failed = 0
- foreach ($result in $resourceTypeGroupedByCAFResourceNamingResult) {
- $resultNameSplitted = $result.Name -split ', '
- if ($resultNameSplitted[0] -eq 'passed') {
- $passed = $result.Count
- }
-
- if ($resultNameSplitted[0] -eq 'failed') {
- $failed = $result.Count
- }
- }
-
- if ($passed -gt 0) {
- $percentage = [math]::Round(($passed / ($passed + $failed) * 100), 2)
- }
- else {
- $percentage = 0
- }
-
- @"
-
-$($entry.Name)
-$($namingConvention)
-$($namingConventionFriendlyName)
-$($passed)
-$($failed)
-$($percentage)%
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsCAFResourceNamingALL)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No CAF Naming Recommendation Compliance data available
-'@)
- }
-
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
- #endregion ScopeInsightsCAFResourceNamingALL
- }
-
- #region ScopeInsightsOrphanedResources
- if ($mgOrSub -eq 'sub') {
- if ($arrayOrphanedResourcesGroupedBySubscription) {
- $orphanedResourcesThisSubscription = $arrayOrphanedResourcesGroupedBySubscription.where({ $_.Name -eq $subscriptionId })
- if ($orphanedResourcesThisSubscription) {
- $orphanedResourcesThisSubscriptionCount = $orphanedResourcesThisSubscription.Group.count
-
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- $orphanedIncludingCost = $true
- $hintTableTH = " ($($AzureConsumptionPeriod) days)"
-
- $orphanedResourcesThisSubscriptionGroupedByType = $orphanedResourcesThisSubscription.Group | Group-Object -Property type, currency
- $orphanedResourcesThisSubscriptionGroupedByTypeCount = ($orphanedResourcesThisSubscriptionGroupedByType | Measure-Object).Count
- }
- else {
- $orphanedIncludingCost = $false
- $hintTableTH = ''
-
- $orphanedResourcesThisSubscriptionGroupedByType = $orphanedResourcesThisSubscription.Group | Group-Object -Property type
- $orphanedResourcesThisSubscriptionGroupedByTypeCount = ($orphanedResourcesThisSubscriptionGroupedByType | Measure-Object).Count
- }
-
- $tfCount = $orphanedResourcesThisSubscriptionGroupedByTypeCount
- $htmlTableId = "ScopeInsights_OrphanedResources_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- $orphanedResourcesThisSubscriptionCount Cost optimization & cleanup ($orphanedResourcesThisSubscriptionGroupedByTypeCount ResourceTypes)
-
-
'Azure Orphan Resources' ARG queries and workbooks GitHub
-
Resource details can be found in the CSV output *_ResourcesCostOptimizationAndCleanup.csv
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Resource count
-Intent
-Cost$($hintTableTH)
-Currency
-
-
-
-"@)
- $htmlScopeInsightsOrphanedResources = $null
- $htmlScopeInsightsOrphanedResources = foreach ($resourceType in $orphanedResourcesThisSubscriptionGroupedByType | Sort-Object -Property Name) {
-
- if ($orphanedIncludingCost) {
- if (($resourceType.Group[0].Intent) -like 'cost savings*') {
- $orphCost = ($resourceType.Group.Cost | Measure-Object -Sum).Sum
- if ($orphCost -eq 0) {
- $orphCost = ''
- }
- $orphCurrency = $resourceType.Group[0].Currency
- }
- else {
- $orphCost = ''
- $orphCurrency = ''
- }
- }
- else {
- if (($resourceType.Group.Intent | Get-Unique) -like 'cost savings*') {
- $orphCost = "use parameter -DoAzureConsumption to show potential savings "
- $orphCurrency = ''
- }
- else {
- $orphCost = ''
- $orphCurrency = ''
- }
- }
-
- @"
-
-$(($resourceType.Name -split ',')[0])
-$($resourceType.Group.Count)
-$($resourceType.Group[0].Intent)
-$($orphCost)
-$($orphCurrency)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsOrphanedResources)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No cost optimization & cleanup
-'@)
- }
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No cost optimization & cleanup
-'@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
- #endregion ScopeInsightsOrphanedResources
-
- #ScopeInsightsDiagnosticsCapable
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- #resourcesDiagnosticsCapable
- #region ScopeInsightsDiagnosticsCapable
- if ($mgOrSub -eq 'mg') {
- $resourceTypesUnique = ($resourcesAllChildSubscriptions | Select-Object type -Unique).type
- $resourceTypesSummarizedArray = [System.Collections.ArrayList]@()
- foreach ($resourceTypeUnique in $resourceTypesUnique) {
- $resourcesTypeCountTotal = 0
- ($resourcesAllChildSubscriptions.where( { $_.type -eq $resourceTypeUnique } )).count_ | ForEach-Object { $resourcesTypeCountTotal += $_ }
- $dataFromResourceTypesDiagnosticsArray = $resourceTypesDiagnosticsArray.where( { $_.ResourceType -eq $resourceTypeUnique } )
- if ($dataFromResourceTypesDiagnosticsArray.Metrics -eq $true -or $dataFromResourceTypesDiagnosticsArray.Logs -eq $true) {
- $resourceDiagnosticscapable = $true
- }
- else {
- $resourceDiagnosticscapable = $false
- }
- $null = $resourceTypesSummarizedArray.Add([PSCustomObject]@{
- ResourceType = $resourceTypeUnique
- ResourceCount = $resourcesTypeCountTotal
- DiagnosticsCapable = $resourceDiagnosticscapable
- Metrics = $dataFromResourceTypesDiagnosticsArray.Metrics
- Logs = $dataFromResourceTypesDiagnosticsArray.Logs
- LogCategories = ($dataFromResourceTypesDiagnosticsArray.LogCategories -join "$CsvDelimiterOpposite ")
- })
- }
- $subscriptionResourceTypesDiagnosticsCapableMetricsCount = ($resourceTypesSummarizedArray.where( { $_.Metrics -eq $true } )).count
- $subscriptionResourceTypesDiagnosticsCapableLogsCount = ($resourceTypesSummarizedArray.where( { $_.Logs -eq $true } )).count
- $subscriptionResourceTypesDiagnosticsCapableMetricsLogsCount = ($resourceTypesSummarizedArray.where( { $_.Metrics -eq $true -or $_.Logs -eq $true } )).count
-
- if ($resourcesAllChildSubscriptionResourceTypeCount -gt 0) {
- $tfCount = $resourcesAllChildSubscriptionResourceTypeCount
- $htmlTableId = "ScopeInsights_resourcesDiagnosticsCapable_$($mgchild -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- $subscriptionResourceTypesDiagnosticsCapableMetricsLogsCount/$resourcesAllChildSubscriptionResourceTypeCount ResourceTypes (1st party) Diagnostics capable ($subscriptionResourceTypesDiagnosticsCapableMetricsCount Metrics, $subscriptionResourceTypesDiagnosticsCapableLogsCount Logs) (all Subscriptions below this scope)
-
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Resource Count
-Diagnostics capable
-Metrics
-Logs
-LogCategories
-
-
-
-"@)
- $htmlScopeInsightsDiagnosticsCapable = $null
- $htmlScopeInsightsDiagnosticsCapable = foreach ($resourceSubscriptionResourceType in $resourceTypesSummarizedArray | Sort-Object @{Expression = { $_.ResourceType } }) {
- @"
-
-$($resourceSubscriptionResourceType.ResourceType)
-$($resourceSubscriptionResourceType.ResourceCount)
-$($resourceSubscriptionResourceType.DiagnosticsCapable)
-$($resourceSubscriptionResourceType.Metrics)
-$($resourceSubscriptionResourceType.Logs)
-$($resourceSubscriptionResourceType.LogCategories)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsDiagnosticsCapable)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $resourcesAllChildSubscriptionResourceTypeCount ResourceTypes (1st party) Diagnostics capable (all Subscriptions below this scope)
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
-
- if ($mgOrSub -eq 'sub') {
- $resourceTypesUnique = ($resourcesSubscription | Select-Object type -Unique).type
- $resourceTypesSummarizedArray = [System.Collections.ArrayList]@()
- foreach ($resourceTypeUnique in $resourceTypesUnique) {
- $resourcesTypeCountTotal = 0
- ($resourcesSubscription.where( { $_.type -eq $resourceTypeUnique } )).count_ | ForEach-Object { $resourcesTypeCountTotal += $_ }
- $dataFromResourceTypesDiagnosticsArray = $resourceTypesDiagnosticsArray.where( { $_.ResourceType -eq $resourceTypeUnique } )
- if ($dataFromResourceTypesDiagnosticsArray.Metrics -eq $true -or $dataFromResourceTypesDiagnosticsArray.Logs -eq $true) {
- $resourceDiagnosticscapable = $true
- }
- else {
- $resourceDiagnosticscapable = $false
- }
- $null = $resourceTypesSummarizedArray.Add([PSCustomObject]@{
- ResourceType = $resourceTypeUnique
- ResourceCount = $resourcesTypeCountTotal
- DiagnosticsCapable = $resourceDiagnosticscapable
- Metrics = $dataFromResourceTypesDiagnosticsArray.Metrics
- Logs = $dataFromResourceTypesDiagnosticsArray.Logs
- LogCategories = ($dataFromResourceTypesDiagnosticsArray.LogCategories -join "$CsvDelimiterOpposite ")
- })
- }
-
- $subscriptionResourceTypesDiagnosticsCapableMetricsCount = ($resourceTypesSummarizedArray.where( { $_.Metrics -eq $true } )).count
- $subscriptionResourceTypesDiagnosticsCapableLogsCount = ($resourceTypesSummarizedArray.where( { $_.Logs -eq $true } )).count
- $subscriptionResourceTypesDiagnosticsCapableMetricsLogsCount = ($resourceTypesSummarizedArray.where( { $_.Metrics -eq $true -or $_.Logs -eq $true } )).count
-
- if ($resourcesSubscriptionResourceTypeCount -gt 0) {
- $tfCount = $resourcesSubscriptionResourceTypeCount
- $htmlTableId = "ScopeInsights_resourcesDiagnosticsCapable_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- $subscriptionResourceTypesDiagnosticsCapableMetricsLogsCount/$resourcesSubscriptionResourceTypeCount ResourceTypes (1st party) Diagnostics capable ($subscriptionResourceTypesDiagnosticsCapableMetricsCount Metrics, $subscriptionResourceTypesDiagnosticsCapableLogsCount Logs)
-
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Resource Count
-Diagnostics capable
-Metrics
-Logs
-LogCategories
-
-
-
-"@)
- $htmlScopeInsightsDiagnosticsCapable = $null
- $htmlScopeInsightsDiagnosticsCapable = foreach ($resourceSubscriptionResourceType in $resourceTypesSummarizedArray | Sort-Object @{Expression = { $_.ResourceType } }) {
- @"
-
-$($resourceSubscriptionResourceType.ResourceType)
-$($resourceSubscriptionResourceType.ResourceCount)
-$($resourceSubscriptionResourceType.DiagnosticsCapable)
-$($resourceSubscriptionResourceType.Metrics)
-$($resourceSubscriptionResourceType.Logs)
-$($resourceSubscriptionResourceType.LogCategories)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsDiagnosticsCapable)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $resourcesSubscriptionResourceTypeCount ResourceTypes (1st party) Diagnostics capable
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
- #endregion ScopeInsightsDiagnosticsCapable
- }
-
- #ScopeInsightsUserAssignedIdentities4Resources
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- if ($mgOrSub -eq 'sub') {
- #region ScopeInsightsUserAssignedIdentities4Resources
- if ($arrayUserAssignedIdentities4ResourcesSubscriptionCount -gt 0) {
- $tfCount = $arrayUserAssignedIdentities4ResourcesSubscriptionCount
- $htmlTableId = "ScopeInsights_UserAssignedIdentities4Resources_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
-
- UserAssigned Managed Identities assigned to Resources / vice versa
-
-
Managed identity 'user-assigned' vs 'system-assigned' docs
-
Download CSV
semicolon |
comma
-
-
-
-MI Name
-MI MgPath
-MI Subscription Name
-MI Subscription Id
-MI ResourceGroup
-MI ResourceId
-MI AAD SP objectId
-MI AAD SP applicationId
-MI count Res assignments
-MI used cross subscription
-Res Name
-Res Type
-Res MgPath
-Res Subscription Name
-Res Subscription Id
-Res ResourceGroup
-Res Id
-Res count assigned MIs
-
-
-
-"@)
- $htmlScopeInsightsUserAssignedIdentities4Resource = $null
- $htmlScopeInsightsUserAssignedIdentities4Resource = foreach ($miResEntry in $arrayUserAssignedIdentities4ResourcesSubscription | Sort-Object -Property miResourceId, resourceId) {
- @"
-
- $($miResEntry.miResourceName)
- $($miResEntry.miMgPath)
- $($miResEntry.miSubscriptionName)
- $($miResEntry.miSubscriptionId)
- $($miResEntry.miResourceGroupName)
- $($miResEntry.miResourceId)
- $($miResEntry.miPrincipalId)
- $($miResEntry.miClientId)
- $($htUserAssignedIdentitiesAssignedResources.($miResEntry.miPrincipalId).ResourcesCount)
- $($miResEntry.miCrossSubscription)
- $($miResEntry.resourceName)
- $($miResEntry.resourceType)
- $($miResEntry.resourceMgPath)
- $($miResEntry.resourceSubscriptionName)
- $($miResEntry.resourceSubscriptionId)
- $($miResEntry.resourceResourceGroupName)
- $($miResEntry.resourceId)
- $($htResourcesAssignedUserAssignedIdentities.(($miResEntry.resourceId).tolower()).UserAssignedIdentitiesCount)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsUserAssignedIdentities4Resource)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No UserAssigned Managed Identities assigned to Resources / vice versa - at all
-'@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsUserAssignedIdentities4Resources
- }
- }
-
- #ScopeInsightsPSRule
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- if ($azAPICallConf['htParameters'].DoPSRule -eq $true) {
- #region ScopeInsightsPSRule
-
- if ($mgOrSub -eq 'mg') {
-
- $allPSRuleResultsUnderThisMg = [system.collections.ArrayList]@()
- foreach ($mg in $grpPSRuleManagementGroups) {
- if ($htManagementGroupsMgPath.($mg.name -replace '.*/').path -contains $mgchild) {
- $allPSRuleResultsUnderThisMg.AddRange($mg.Group)
- }
- }
-
- $grpThisManagementGroup = $allPSRuleResultsUnderThisMg | Group-Object -Property resourceType, pillar, category, severity, rule, result
-
- if ($grpThisManagementGroup) {
- $grpThisManagementGroupCount = $grpThisManagementGroup.Count
- $tfCount = $grpThisManagementGroupCount
- $htmlTableId = "ScopeInsights_PSRule_$($mgchild -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
-
- $grpThisManagementGroupCount 'PSRule for Azure' results
-
-
Learn about PSRule for Azure
-
Download CSV
semicolon |
comma
-
-
-
-Resource Type
-Resource Count
-Subscription Count
-Pillar
-Category
-Severity
-Rule
-Recommendation
-lnk
-State
-
-
-
-"@)
- $htmlScopeInsightsPSRuleMG = $null
- $htmlScopeInsightsPSRuleMG = foreach ($result in $grpThisManagementGroup) {
- $resultNameSplit = $result.Name.split(', ')
- @"
-
- $($resultNameSplit[0])
- $($result.Group.Count)
- $(($result.Group.subscriptionId | Sort-Object -Unique).Count)
- $($resultNameSplit[1])
- $($resultNameSplit[2])
- $($resultNameSplit[3])
- $(($result.Group[0].rule))
- $(($result.Group[0].recommendation))
-
- $($resultNameSplit[5])
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsPSRuleMG)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
-
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No PSRule for Azure results
-'@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
-
- if ($mgOrSub -eq 'sub') {
- $grpThisSubscription = $grpPSRuleSubscriptions.where({ $_.Name -eq $subscriptionId })
- $grpThisSubscriptionGrouped = $grpThisSubscription.Group | Group-Object -Property resourceType, pillar, category, severity, rule, result
-
- if ($grpThisSubscriptionGrouped) {
- $grpThisSubscriptionGroupedCount = $grpThisSubscriptionGrouped.Count
- $tfCount = $grpThisSubscriptionGroupedCount
- $htmlTableId = "ScopeInsights_PSRule_$($subscriptionId -replace '-','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
-
- $grpThisSubscriptionGroupedCount PSRule for Azure results
-
-
Learn about PSRule for Azure
-
Download CSV
semicolon |
comma
-
-
-
-Resource Type
-Resource Count
-Pillar
-Category
-Severity
-Rule
-Recommendation
-lnk
-State
-
-
-
-"@)
- $htmlScopeInsightsPSRuleSub = $null
- $htmlScopeInsightsPSRuleSub = foreach ($result in $grpThisSubscriptionGrouped) {
- $resultNameSplit = $result.Name.split(', ')
- @"
-
- $($resultNameSplit[0])
- $($result.Group.Count)
- $($resultNameSplit[1])
- $($resultNameSplit[2])
- $($resultNameSplit[3])
- $(($result.Group[0].rule))
- $(($result.Group[0].recommendation))
-
- $($resultNameSplit[5])
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsPSRuleSub)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
-
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No PSRule results
-'@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
- #endregion ScopeInsightsPSRule
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- PSRule for Azure - integration paused - PSRule for Azure
-'@)
- }
- }
-
- #PolicyAssignments
- #region ScopeInsightsPolicyAssignments
- if ($mgOrSub -eq 'mg') {
- $SIDivContentClass = 'contentSIMG'
- $htmlTableIdentifier = $mgChild
-
- $policiesAssigned = [System.Collections.ArrayList]@()
- $policiesCount = 0
- $policiesCountBuiltin = 0
- $policiesCountCustom = 0
- $policiesAssignedAtScope = 0
- $policiesInherited = 0
- foreach ($policyAssignment in $arrayPolicyAssignmentsEnrichedForThisManagementGroupVariantPolicy) {
- if ([String]::IsNullOrEmpty($policyAssignment.subscriptionId)) {
- $null = $policiesAssigned.Add($policyAssignment)
- $policiesCount++
- if ($policyAssignment.PolicyType -eq 'BuiltIn') {
- $policiesCountBuiltin++
- }
- if ($policyAssignment.PolicyType -eq 'Custom') {
- $policiesCountCustom++
- }
- if ($policyAssignment.Inheritance -like 'this*') {
- $policiesAssignedAtScope++
- }
- if ($policyAssignment.Inheritance -notlike 'this*') {
- $policiesInherited++
- }
- }
- }
- }
- if ($mgOrSub -eq 'sub') {
- $SIDivContentClass = 'contentSISub'
- $htmlTableIdentifier = $subscriptionId
-
- $policiesAssigned = [System.Collections.ArrayList]@()
- $policiesCount = 0
- $policiesCountBuiltin = 0
- $policiesCountCustom = 0
- $policiesAssignedAtScope = 0
- $policiesInherited = 0
- foreach ($policyAssignment in $arrayPolicyAssignmentsEnrichedForThisSubscriptionVariantPolicy) {
- $null = $policiesAssigned.Add($policyAssignment)
- $policiesCount++
- if ($policyAssignment.PolicyType -eq 'BuiltIn') {
- $policiesCountBuiltin++
- }
- if ($policyAssignment.PolicyType -eq 'Custom') {
- $policiesCountCustom++
- }
- if ($policyAssignment.Inheritance -like 'this*') {
- $policiesAssignedAtScope++
- }
- if ($policyAssignment.Inheritance -notlike 'this*') {
- $policiesInherited++
- }
- }
- }
-
- if (($policiesAssigned).count -gt 0) {
- $tfCount = ($policiesAssigned).count
- $htmlTableId = "ScopeInsights_PolicyAssignments_$($htmlTableIdentifier -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- $noteOrNot = ''
- [void]$htmlScopeInsights.AppendLine(@"
- $policiesCount Policy assignments ($policiesAssignedAtScope at scope, $policiesInherited inherited) (Builtin: $policiesCountBuiltin | Custom: $policiesCountCustom)
-
-
Download CSV
semicolon |
comma
-
*Depending on the number of rows and your computer´s performance the table may respond with delay, download the csv for better filtering experience
-
-
-
-Inheritance
-ScopeExcluded
-Exemption applies
-Policy DisplayName
-PolicyId
-Type
-Category
-ALZ
-Effect
-Parameters
-Enforcement
-NonCompliance Message
-"@)
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
-
- [void]$htmlScopeInsights.AppendLine(@'
-Policies NonCmplnt
-Policies Compliant
-Resources NonCmplnt
-Resources Compliant
-Resources Conflicting
-'@)
- }
-
- [void]$htmlScopeInsights.AppendLine(@"
-Role/Assignment $noteOrNot
-Managed Identity
-Assignment DisplayName
-AssignmentId
-AssignedBy
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlScopeInsightsPolicyAssignments = $null
- $htmlScopeInsightsPolicyAssignments = foreach ($policyAssignment in $policiesAssigned | Sort-Object @{Expression = { $_.Level } }, @{Expression = { $_.MgName } }, @{Expression = { $_.MgId } }, @{Expression = { $_.SubscriptionName } }, @{Expression = { $_.SubscriptionId } }, @{Expression = { $_.PolicyAssignmentId } }) {
-
- if ($policyAssignment.PolicyType -eq 'Custom') {
- $policyName = ($policyAssignment.PolicyName -replace '<', '<' -replace '>', '>')
- }
- else {
- $policyName = $policyAssignment.PolicyName
- }
- @"
-
-$($policyAssignment.Inheritance)
-$($policyAssignment.ExcludedScope)
-$($policyAssignment.ExemptionScope)
-$($policyName)
-$($policyAssignment.PolicyId)
-$($policyAssignment.PolicyType)
-$($policyAssignment.PolicyCategory -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyIsALZ)
-$($policyAssignment.Effect)
-$($policyAssignment.PolicyAssignmentParameters)
-$($policyAssignment.PolicyAssignmentEnforcementMode)
-$($policyAssignment.PolicyAssignmentNonComplianceMessages)
-"@
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- @"
-$($policyAssignment.NonCompliantPolicies)
-$($policyAssignment.CompliantPolicies)
-$($policyAssignment.NonCompliantResources)
-$($policyAssignment.CompliantResources)
-$($policyAssignment.ConflictingResources)
-"@
- }
-
- @"
-$($policyAssignment.RelatedRoleAssignments)
-$($policyAssignment.PolicyAssignmentMI)
-$($policyAssignment.PolicyAssignmentDisplayName -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyAssignmentId -replace '<', '<' -replace '>', '>')
-$($policyAssignment.AssignedBy)
-$($policyAssignment.CreatedOn)
-$($policyAssignment.CreatedBy)
-$($policyAssignment.UpdatedOn)
-$($policyAssignment.UpdatedBy)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsPolicyAssignments)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $(($policiesAssigned).count) Policy assignments
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsPolicyAssignments
-
- #PolicySetAssignments
- #region ScopeInsightsPolicySetAssignments
- if ($mgOrSub -eq 'mg') {
- $SIDivContentClass = 'contentSIMG'
- $htmlTableIdentifier = $mgChild
-
- $policySetsAssigned = [System.Collections.ArrayList]@()
- $policySetsCount = 0
- $policySetsCountBuiltin = 0
- $policySetsCountCustom = 0
- $policySetsAssignedAtScope = 0
- $policySetsInherited = 0
- foreach ($policySetAssignment in $arrayPolicyAssignmentsEnrichedForThisManagementGroupVariantPolicySet) {
- if ([String]::IsNullOrEmpty($policySetAssignment.subscriptionId)) {
- $null = $policySetsAssigned.Add($policySetAssignment)
- $policySetsCount++
- if ($policySetAssignment.PolicyType -eq 'BuiltIn') {
- $policySetsCountBuiltin++
- }
- if ($policySetAssignment.PolicyType -eq 'Custom') {
- $policySetsCountCustom++
- }
- if ($policySetAssignment.Inheritance -like 'this*') {
- $policySetsAssignedAtScope++
- }
- if ($policySetAssignment.Inheritance -notlike 'this*') {
- $policySetsInherited++
- }
- }
- }
- }
- if ($mgOrSub -eq 'sub') {
- $SIDivContentClass = 'contentSISub'
- $htmlTableIdentifier = $subscriptionId
-
- $policySetsAssigned = [System.Collections.ArrayList]@()
- $policySetsCount = 0
- $policySetsCountBuiltin = 0
- $policySetsCountCustom = 0
- $policySetsAssignedAtScope = 0
- $policySetsInherited = 0
- foreach ($policySetAssignment in $arrayPolicyAssignmentsEnrichedForThisSubscriptionVariantPolicySet) {
- $null = $policySetsAssigned.Add($policySetAssignment)
- $policySetsCount++
- if ($policySetAssignment.PolicyType -eq 'BuiltIn') {
- $policySetsCountBuiltin++
- }
- if ($policySetAssignment.PolicyType -eq 'Custom') {
- $policySetsCountCustom++
- }
- if ($policySetAssignment.Inheritance -like 'this*') {
- $policySetsAssignedAtScope++
- }
- if ($policySetAssignment.Inheritance -notlike 'this*') {
- $policySetsInherited++
- }
- }
- }
-
- if (($policySetsAssigned).count -gt 0) {
- $tfCount = ($policiesAssigned).count
- $htmlTableId = "ScopeInsights_PolicySetAssignments_$($htmlTableIdentifier -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- $noteOrNot = ''
- [void]$htmlScopeInsights.AppendLine(@"
- $policySetsCount PolicySet assignments ($policySetsAssignedAtScope at scope, $policySetsInherited inherited) (Builtin: $policySetsCountBuiltin | Custom: $policySetsCountCustom)
-
-
Download CSV
semicolon |
comma
-
-
-
-Inheritance
-ScopeExcluded
-PolicySet DisplayName
-PolicySetId
-Type
-Category
-ALZ
-Parameters
-Enforcement
-NonCompliance Message
-"@)
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
-
- [void]$htmlScopeInsights.AppendLine(@'
-Policies NonCmplnt
-Policies Compliant
-Resources NonCmplnt
-Resources Compliant
-Resources Conflicting
-'@)
- }
-
- [void]$htmlScopeInsights.AppendLine(@"
-Role/Assignment $noteOrNot
-Managed Identity
-Assignment DisplayName
-AssignmentId
-AssignedBy
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlScopeInsightsPolicySetAssignments = $null
- $htmlScopeInsightsPolicySetAssignments = foreach ($policyAssignment in $policySetsAssigned | Sort-Object -Property Level, PolicyAssignmentId) {
- if ($policyAssignment.PolicyType -eq 'Custom') {
- $policyName = ($policyAssignment.PolicyName -replace '<', '<' -replace '>', '>')
- }
- else {
- $policyName = $policyAssignment.PolicyName
- }
- @"
-
-$($policyAssignment.Inheritance)
-$($policyAssignment.ExcludedScope)
-$($policyName)
-$($policyAssignment.PolicyId)
-$($policyAssignment.PolicyType)
-$($policyAssignment.PolicyCategory -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyIsALZ)
-$($policyAssignment.PolicyAssignmentParameters)
-$($policyAssignment.PolicyAssignmentEnforcementMode)
-$($policyAssignment.PolicyAssignmentNonComplianceMessages)
-"@
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- @"
-$($policyAssignment.NonCompliantPolicies)
-$($policyAssignment.CompliantPolicies)
-$($policyAssignment.NonCompliantResources)
-$($policyAssignment.CompliantResources)
-$($policyAssignment.ConflictingResources)
-"@
- }
- @"
-$($policyAssignment.RelatedRoleAssignments)
-$($policyAssignment.PolicyAssignmentMI)
-$($policyAssignment.PolicyAssignmentDisplayName -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyAssignmentId -replace '<', '<' -replace '>', '>')
-$($policyAssignment.AssignedBy)
-$($policyAssignment.CreatedOn)
-$($policyAssignment.CreatedBy)
-$($policyAssignment.UpdatedOn)
-$($policyAssignment.UpdatedBy)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsPolicySetAssignments)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $(($policySetsAssigned).count) PolicySet assignments
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsPolicySetAssignments
-
- #PolicyAssignmentsLimit (Policy+PolicySet)
- #region ScopeInsightsPolicyAssignmentsLimit
- if ($mgOrSub -eq 'mg') {
- $limit = $LimitPOLICYPolicyAssignmentsManagementGroup
- }
- if ($mgOrSub -eq 'sub') {
- $limit = $LimitPOLICYPolicyAssignmentsSubscription
- }
-
- if ($policiesAssignedAtScope -eq 0 -and $policySetsAssignedAtScope -eq 0) {
- $faimage = " "
-
- [void]$htmlScopeInsights.AppendLine(@"
- $faImage Policy Assignment Limit: 0/$limit
-"@)
- }
- else {
- if ($mgOrSub -eq 'mg') {
- $scopePolicyAssignmentsLimit = $policyPolicyBaseQueryScopeInsights.where( { [String]::IsNullOrEmpty($_.SubscriptionId) -and $_.MgId -eq $mgChild } )
- }
- if ($mgOrSub -eq 'sub') {
- $scopePolicyAssignmentsLimit = $policyPolicyBaseQueryScopeInsights.where( { $_.SubscriptionId -eq $subscriptionId } )
- }
-
- if ($scopePolicyAssignmentsLimit.PolicyAndPolicySetAssignmentAtScopeCount -gt (($limit) * $LimitCriticalPercentage / 100)) {
- $faImage = " "
- }
- else {
- $faimage = " "
- }
- [void]$htmlScopeInsights.AppendLine(@"
- $faImage Policy Assignment Limit: $($scopePolicyAssignmentsLimit.PolicyAndPolicySetAssignmentAtScopeCount)/$($limit)
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsPolicyAssignmentsLimit
-
- #ScopedPolicies
- #region ScopeInsightsScopedPolicies
- if ($mgOrSub -eq 'mg') {
- $SIDivContentClass = 'contentSIMG'
- $htmlTableIdentifier = $mgChild
- $scopePolicies = $customPoliciesDetailed.where( { $_.PolicyDefinitionId -like "*/providers/Microsoft.Management/managementGroups/$mgChild/*" } )
- $scopePoliciesCount = ($scopePolicies).count
- }
- if ($mgOrSub -eq 'sub') {
- $SIDivContentClass = 'contentSISub'
- $htmlTableIdentifier = $subscriptionId
- $scopePolicies = $customPoliciesDetailed.where( { $_.PolicyDefinitionId -like "*/subscriptions/$subscriptionId/*" } )
- $scopePoliciesCount = ($scopePolicies).count
- }
-
- if ($scopePoliciesCount -gt 0) {
- $tfCount = $scopePoliciesCount
- $htmlTableId = "ScopeInsights_ScopedPolicies_$($htmlTableIdentifier -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- if ($mgOrSub -eq 'mg') {
- $LimitPOLICYPolicyScoped = $LimitPOLICYPolicyDefinitionsScopedManagementGroup
- if ($scopePoliciesCount -gt (($LimitPOLICYPolicyScoped * $LimitCriticalPercentage) / 100)) {
- $faIcon = " "
- }
- else {
- $faIcon = " "
- }
- }
- if ($mgOrSub -eq 'sub') {
- $LimitPOLICYPolicyScoped = $LimitPOLICYPolicyDefinitionsScopedSubscription
- if ($scopePoliciesCount -gt (($LimitPOLICYPolicyScoped * $LimitCriticalPercentage) / 100)) {
- $faIcon = " "
- }
- else {
- $faIcon = " "
- }
- }
-
- [void]$htmlScopeInsights.AppendLine(@"
-$faIcon $scopePoliciesCount Custom Policy definitions scoped | Limit: ($scopePoliciesCount/$LimitPOLICYPolicyScoped)
-
-
Download CSV
semicolon |
comma
-
-
-
-Policy DisplayName
-PolicyId
-Category
-ALZ
-Policy effect
-Role definitions
-Unique assignments
-Used in PolicySets
-
-
-
-"@)
- $htmlScopeInsightsScopedPolicies = $null
- $htmlScopeInsightsScopedPolicies = foreach ($custompolicy in $scopePolicies | Sort-Object @{Expression = { $_.PolicyDisplayName } }, @{Expression = { $_.PolicyDefinitionId } }) {
- if ($custompolicy.UsedInPolicySetsCount -gt 0) {
- $customPolicyUsedInPolicySets = "$($customPolicy.UsedInPolicySetsCount) ($($customPolicy.UsedInPolicySets))"
- }
- else {
- $customPolicyUsedInPolicySets = $($customPolicy.UsedInPolicySetsCount)
- }
- @"
-
-$($customPolicy.PolicyDisplayName -replace '<', '<' -replace '>', '>')
-$($customPolicy.PolicyDefinitionId -replace '<', '<' -replace '>', '>')
-$($customPolicy.PolicyCategory -replace '<', '<' -replace '>', '>')
-$($customPolicy.ALZ)
-$($customPolicy.PolicyEffect)
-$($customPolicy.RoleDefinitions)
-$($customPolicy.UniqueAssignments -replace '<', '<' -replace '>', '>')
-$($customPolicyUsedInPolicySets)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsScopedPolicies)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $scopePoliciesCount Custom Policy definitions scoped
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsScopedPolicies
-
- #ScopedPolicySets
- #region ScopeInsightsScopedPolicySets
- if ($mgOrSub -eq 'mg') {
- $SIDivContentClass = 'contentSIMG'
- $htmlTableIdentifier = $mgChild
- $scopePolicySets = $customPolicySetsDetailed.where( { $_.PolicySetDefinitionId -like "*/providers/Microsoft.Management/managementGroups/$mgChild/*" } )
- $scopePolicySetsCount = ($scopePolicySets).count
- }
- if ($mgOrSub -eq 'sub') {
- $SIDivContentClass = 'contentSISub'
- $htmlTableIdentifier = $subscriptionId
- $scopePolicySets = $customPolicySetsDetailed.where( { $_.PolicySetDefinitionId -like "*/subscriptions/$subscriptionId/*" } )
- $scopePolicySetsCount = ($scopePolicySets).count
- }
-
- if ($scopePolicySetsCount -gt 0) {
- $tfCount = $scopePolicySetsCount
- $htmlTableId = "ScopeInsights_ScopedPolicySets_$($htmlTableIdentifier -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- if ($mgOrSub -eq 'mg') {
- $LimitPOLICYPolicySetScoped = $LimitPOLICYPolicySetDefinitionsScopedManagementGroup
- if ($scopePolicySetsCount -gt (($LimitPOLICYPolicySetScoped * $LimitCriticalPercentage) / 100)) {
- $faIcon = " "
- }
- else {
- $faIcon = " "
- }
- }
- if ($mgOrSub -eq 'sub') {
- $LimitPOLICYPolicySetScoped = $LimitPOLICYPolicySetDefinitionsScopedSubscription
- if ($scopePolicySetsCount -gt (($LimitPOLICYPolicySetScoped * $LimitCriticalPercentage) / 100)) {
- $faIcon = " "
- }
- else {
- $faIcon = " "
- }
- }
- [void]$htmlScopeInsights.AppendLine(@"
-$faIcon $scopePolicySetsCount Custom PolicySet definitions scoped | Limit: ($scopePolicySetsCount/$LimitPOLICYPolicySetScoped)
-
-
Download CSV
semicolon |
comma
-
-
-
-PolicySet DisplayName
-PolicySetId
-Category
-ALZ
-Unique assignments
-Policies Used
-
-
-
-"@)
- $htmlScopeInsightsScopedPolicySets = $null
- $htmlScopeInsightsScopedPolicySets = foreach ($custompolicySet in $scopePolicySets | Sort-Object @{Expression = { $_.PolicySetDisplayName } }, @{Expression = { $_.PolicySetDefinitionId } }) {
- @"
-
-$($custompolicySet.PolicySetDisplayName -replace '<', '<' -replace '>', '>')
-$($custompolicySet.PolicySetDefinitionId -replace '<', '<' -replace '>', '>')
-$($custompolicySet.PolicySetCategory -replace '<', '<' -replace '>', '>')
-$($custompolicySet.ALZ)
-$($custompolicySet.UniqueAssignments -replace '<', '<' -replace '>', '>')
-$($custompolicySet.PoliciesUsed)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsScopedPolicySets)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $scopePolicySetsCount Custom PolicySet definitions scoped
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsScopedPolicySets
-
- #BlueprintAssignments
- #region ScopeInsightsBlueprintAssignments
- if ($mgOrSub -eq 'sub') {
- if ($blueprintsAssignedCount -gt 0) {
-
- if ($mgOrSub -eq 'mg') {
- $htmlTableIdentifier = $mgChild
- }
- if ($mgOrSub -eq 'sub') {
- $htmlTableIdentifier = $subscriptionId
- }
- $htmlTableId = "ScopeInsights_BlueprintAssignment_$($htmlTableIdentifier -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- $blueprintsAssignedCount Blueprints assigned
-
-
Download CSV
semicolon |
comma
-
-
-
-Blueprint Name
-Blueprint DisplayName
-Blueprint Description
-BlueprintId
-Blueprint Version
-Blueprint AssignmentId
-
-
-
-"@)
- $htmlScopeInsightsBlueprintAssignments = $null
- $htmlScopeInsightsBlueprintAssignments = foreach ($blueprintAssigned in $blueprintsAssigned) {
- @"
-
-$($blueprintAssigned.BlueprintName -replace '<', '<' -replace '>', '>')
-$($blueprintAssigned.BlueprintDisplayName -replace '<', '<' -replace '>', '>')
-$($blueprintAssigned.BlueprintDescription -replace '<', '<' -replace '>', '>')
-$($blueprintAssigned.BlueprintId -replace '<', '<' -replace '>', '>')
-$($blueprintAssigned.BlueprintAssignmentVersion -replace '<', '<' -replace '>', '>')
-$($blueprintAssigned.BlueprintAssignmentId -replace '<', '<' -replace '>', '>')
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsBlueprintAssignments)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $blueprintsAssignedCount Blueprints assigned
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- }
- #endregion ScopeInsightsBlueprintAssignments
-
- #BlueprintsScoped
- #region ScopeInsightsBlueprintsScoped
- if ($blueprintsScopedCount -gt 0) {
- $tfCount = $blueprintsScopedCount
- if ($mgOrSub -eq 'mg') {
- $SIDivContentClass = 'contentSIMG'
- $htmlTableIdentifier = $mgChild
- }
- if ($mgOrSub -eq 'sub') {
- $SIDivContentClass = 'contentSISub'
- $htmlTableIdentifier = $subscriptionId
- }
- $htmlTableId = "ScopeInsights_BlueprintScoped_$($htmlTableIdentifier -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- $blueprintsScopedCount Blueprints scoped
-
-
Download CSV
semicolon |
comma
-
-
-
-Blueprint Name
-Blueprint DisplayName
-Blueprint Description
-BlueprintId
-
-
-
-"@)
- $htmlScopeInsightsBlueprintsScoped = $null
- $htmlScopeInsightsBlueprintsScoped = foreach ($blueprintScoped in $blueprintsScoped) {
- @"
-
-$($blueprintScoped.BlueprintName -replace '<', '<' -replace '>', '>')
-$($blueprintScoped.BlueprintDisplayName -replace '<', '<' -replace '>', '>')
-$($blueprintScoped.BlueprintDescription -replace '<', '<' -replace '>', '>')
-$($blueprintScoped.BlueprintId -replace '<', '<' -replace '>', '>')
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsBlueprintsScoped)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $blueprintsScopedCount Blueprints scoped
-"@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsBlueprintsScoped
-
- if ($mgOrSub -eq 'sub') {
- #region ScopeInsightsClassicAdministrators
- if ($htClassicAdministrators.($subscriptionId).ClassicAdministrators.Count -gt 0) {
- $tfCount = $htClassicAdministrators.($subscriptionId).ClassicAdministrators.Count
- $htmlTableId = "ScopeInsights_ClassicAdministrators_$($subscriptionId -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- [void]$htmlScopeInsights.AppendLine(@"
- $tfCount Classic Administrators
-
-
Download CSV
semicolon |
comma
-
-
-
-Role
-Identity
-
-
-
-"@)
- $htmlScopeInsightsClassicAdministrators = $null
- $htmlScopeInsightsClassicAdministrators = foreach ($classicAdministrator in $htClassicAdministrators.($subscriptionId).ClassicAdministrators | Sort-Object -Property Role, Identity) {
- @"
-
-$($classicAdministrator.Role)
-$($classicAdministrator.Identity)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsClassicAdministrators)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@'
- No Classic Administrators
-'@)
- }
- [void]$htmlScopeInsights.AppendLine(@'
-
-
-'@)
- #endregion ScopeInsightsClassicAdministrators
- }
-
- #RoleAssignments
- #region ScopeInsightsRoleAssignments
- if ($mgOrSub -eq 'mg') {
- $SIDivContentClass = 'contentSIMG'
- $htmlTableIdentifier = $mgChild
- $LimitRoleAssignmentsScope = $LimitRBACRoleAssignmentsManagementGroup
-
- $rolesAssigned = [System.Collections.ArrayList]@()
- $rolesAssignedCount = 0
- $rolesAssignedInheritedCount = 0
- $rolesAssignedUser = 0
- $rolesAssignedGroup = 0
- $rolesAssignedServicePrincipal = 0
- $rolesAssignedUnknown = 0
- $roleAssignmentsRelatedToPolicyCount = 0
- $roleSecurityFindingCustomRoleOwner = 0
- $roleSecurityFindingOwnerAssignmentSP = 0
- $rbacForThisManagementGroup = ($rbacAllGroupedByManagementGroup.where( { $_.name -eq $mgChild } )).group
- foreach ($roleAssignment in $rbacForThisManagementGroup) {
- if ([String]::IsNullOrEmpty($roleAssignment.subscriptionId)) {
- $null = $rolesAssigned.Add($roleAssignment)
- $rolesAssignedCount++
- if ($roleAssignment.Scope -notlike 'this*') {
- $rolesAssignedInheritedCount++
- }
- if ($roleAssignment.ObjectType -like 'User*') {
- $rolesAssignedUser++
- }
- if ($roleAssignment.ObjectType -eq 'Group') {
- $rolesAssignedGroup++
- }
- if ($roleAssignment.ObjectType -like 'SP*') {
- $rolesAssignedServicePrincipal++
- }
- if ($roleAssignment.ObjectType -eq 'Unknown') {
- $rolesAssignedUnknown++
- }
- if ($roleAssignment.RbacRelatedPolicyAssignment -ne 'none') {
- $roleAssignmentsRelatedToPolicyCount++
- }
- if ($roleAssignment.RoleSecurityCustomRoleOwner -eq 1) {
- $roleSecurityFindingCustomRoleOwner++
- }
- if ($roleAssignment.RoleSecurityOwnerAssignmentSP -eq 1) {
- $roleSecurityFindingOwnerAssignmentSP++
- }
- }
- }
- }
- if ($mgOrSub -eq 'sub') {
- $SIDivContentClass = 'contentSISub'
- $htmlTableIdentifier = $subscriptionId
- $LimitRoleAssignmentsScope = $htSubscriptionsRoleAssignmentLimit.($subscriptionId)
-
- $rolesAssigned = [System.Collections.ArrayList]@()
- $rolesAssignedCount = 0
- $rolesAssignedInheritedCount = 0
- $rolesAssignedUser = 0
- $rolesAssignedGroup = 0
- $rolesAssignedServicePrincipal = 0
- $rolesAssignedUnknown = 0
- $roleAssignmentsRelatedToPolicyCount = 0
- $roleSecurityFindingCustomRoleOwner = 0
- $roleSecurityFindingOwnerAssignmentSP = 0
- $rbacForThisSubscription = ($rbacAllGroupedBySubscription.where( { $_.name -eq $subscriptionId } )).group
- $rolesAssigned = foreach ($roleAssignment in $rbacForThisSubscription) {
-
- $roleAssignment
- $rolesAssignedCount++
- if ($roleAssignment.Scope -notlike 'this*') {
- $rolesAssignedInheritedCount++
- }
- if ($roleAssignment.ObjectType -like 'User*') {
- $rolesAssignedUser++
- }
- if ($roleAssignment.ObjectType -eq 'Group') {
- $rolesAssignedGroup++
- }
- if ($roleAssignment.ObjectType -like 'SP*') {
- $rolesAssignedServicePrincipal++
- }
- if ($roleAssignment.ObjectType -eq 'Unknown') {
- $rolesAssignedUnknown++
- }
- if ($roleAssignment.RbacRelatedPolicyAssignment -ne 'none') {
- $roleAssignmentsRelatedToPolicyCount++
- }
- if ($roleAssignment.RoleSecurityCustomRoleOwner -eq 1) {
- $roleSecurityFindingCustomRoleOwner++
- }
- if ($roleAssignment.RoleSecurityOwnerAssignmentSP -eq 1) {
- $roleSecurityFindingOwnerAssignmentSP++
- }
- }
- }
-
- $rolesAssignedAtScopeCount = $rolesAssignedCount - $rolesAssignedInheritedCount
-
- if (($rolesAssigned).count -gt 0) {
- $tfCount = ($rolesAssigned).count
- $htmlTableId = "ScopeInsights_RoleAssignments_$($htmlTableIdentifier -replace '\(','_' -replace '\)','_' -replace '-','_' -replace '\.','_')"
- $randomFunctionName = "func_$htmlTableId"
- $noteOrNot = ''
- [void]$htmlScopeInsights.AppendLine(@"
- $rolesAssignedCount Role assignments ($rolesAssignedInheritedCount inherited) (User: $rolesAssignedUser | Group: $rolesAssignedGroup | ServicePrincipal: $rolesAssignedServicePrincipal | Orphaned: $rolesAssignedUnknown) ($($roleSecurityFindingCustomRoleOwnerImg)CustomRoleOwner: $roleSecurityFindingCustomRoleOwner, $($RoleSecurityFindingOwnerAssignmentSPImg)OwnerAssignmentSP: $roleSecurityFindingOwnerAssignmentSP) (Policy related: $roleAssignmentsRelatedToPolicyCount) | Limit: ($rolesAssignedAtScopeCount/$LimitRoleAssignmentsScope)
-
-
Download CSV
semicolon |
comma
-
*Depending on the number of rows and your computer´s performance the table may respond with delay, download the csv for better filtering experience
-
-
-
-Scope
-Role
-RoleId
-Role Type
-Data
-Can do Role assignment
-Identity Displayname
-Identity SignInName
-Identity ObjectId
-Identity Type
-Applicability
-Applies through membership
-Group Details
-Role AssignmentId
-Related Policy Assignment $noteOrNot
-CreatedOn
-CreatedBy
-
-
-
-"@)
- $htmlScopeInsightsRoleAssignments = $null
- $htmlScopeInsightsRoleAssignments = foreach ($roleAssignment in ($rolesAssigned | Sort-Object -Property Level, MgName, MgId, SubscriptionName, SubscriptionId, Scope, Role, RoleId, ObjectId, RoleAssignmentId)) {
- @"
-
-$($roleAssignment.Scope)
-$($roleAssignment.Role)
-$($roleAssignment.RoleId)
-$($roleAssignment.RoleType)
-$($roleAssignment.RoleDataRelated)
-$($roleAssignment.RoleCanDoRoleAssignments)
-$($roleAssignment.ObjectDisplayName)
-$($roleAssignment.ObjectSignInName)
-$($roleAssignment.ObjectId)
-$($roleAssignment.ObjectType)
-$($roleAssignment.AssignmentType)
-$($roleAssignment.AssignmentInheritFrom)
-$($roleAssignment.GroupMembersCount)
-$($roleAssignment.RoleAssignmentId)
-$($roleAssignment.rbacRelatedPolicyAssignment)
-$($roleAssignment.CreatedOn)
-$($roleAssignment.CreatedBy)
-
-"@
- }
- [void]$htmlScopeInsights.AppendLine($htmlScopeInsightsRoleAssignments)
- [void]$htmlScopeInsights.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlScopeInsights.AppendLine(@"
- $(($rbacAll).count) Role assignments
-
-"@)
- }
-
- [void]$htmlScopeInsights.AppendLine(@'
-
-'@)
- #endregion ScopeInsightsRoleAssignments
-
-
- if (-not $NoScopeInsights) {
- $script:html += $htmlScopeInsights
- }
-
- if (-not $NoSingleSubscriptionOutput) {
- if ($mgOrSub -eq 'sub') {
- $htmlThisSubSingleOutput = $htmlSubscriptionOnlyStart
- $htmlThisSubSingleOutput += $htmlScopeInsights
- $htmlThisSubSingleOutput += $htmlSubscriptionOnlyEnd
- $htmlThisSubSingleOutput | Set-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($HTMLPath)$($DirectorySeparatorChar)$($fileName)_$($subscriptionId).html" -Encoding utf8 -Force
- $htmlThisSubSingleOutput = $null
- }
- }
-
- if (-not $NoScopeInsights) {
- if ($scopescnter % 50 -eq 0) {
- $script:scopescnter = 0
- Write-Host ' append file duration: '(Measure-Command { $script:html | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force }).TotalSeconds 'seconds'
- $script:html = $null
- }
- }
-
- if ($scopescnter % 50 -eq 0) {
- showMemoryUsage
- }
-
-}
-function processStorageAccountAnalysis {
- $start = Get-Date
- Write-Host 'Processing Storage Account Analysis'
- $storageAccountsCount = $storageAccounts.count
- if ($storageAccountsCount -gt 0) {
- Write-Host " Executing Storage Account Analysis for $storageAccountsCount Storage Accounts"
- createBearerToken -AzAPICallConfiguration $azapicallconf -targetEndPoint 'Storage'
-
- $htSACost = @{}
- if ($DoAzureConsumption -eq $true) {
- $saConsumptionByResourceId = $allConsumptionData.where({ $_.resourceType -eq 'microsoft.storage/storageaccounts' }) | Group-Object -Property resourceid
-
- foreach ($sa in $saConsumptionByResourceId) {
- $htSACost.($sa.Name) = @{}
- $htSACost.($sa.Name).meterCategoryAll = ($sa.Group.MeterCategory | Sort-Object) -join ', '
- $htSACost.($sa.Name).costAll = ($sa.Group.PreTaxCost | Measure-Object -Sum).Sum #[decimal]($sa.Group.PreTaxCost | Measure-Object -Sum).Sum
- $htSACost.($sa.Name).currencyAll = ($sa.Group.Currency | Sort-Object -Unique) -join ', '
- foreach ($costentry in $sa.Group) {
- $htSACost.($sa.Name)."cost_$($costentry.MeterCategory)" = $costentry.PreTaxCost
- $htSACost.($sa.Name)."currency_$($costentry.MeterCategory)" = $costentry.Currency
- }
- }
- }
-
- $storageAccounts | ForEach-Object -Parallel {
- $storageAccount = $_
- $azAPICallConf = $using:azAPICallConf
- $arrayStorageAccountAnalysisResults = $using:arrayStorageAccountAnalysisResults
- $htAllSubscriptionsFromAPI = $using:htAllSubscriptionsFromAPI
- $htSubscriptionsMgPath = $using:htSubscriptionsMgPath
- $htSubscriptionTags = $using:htSubscriptionTags
- $CSVDelimiterOpposite = $using:CSVDelimiterOpposite
- $htSACost = $using:htSACost
- $StorageAccountAccessAnalysisSubscriptionTags = $using:StorageAccountAccessAnalysisSubscriptionTags
- $StorageAccountAccessAnalysisStorageAccountTags = $using:StorageAccountAccessAnalysisStorageAccountTags
- $listContainersSuccess = 'n/a'
- $containersCount = 'n/a'
- $arrayContainers = @()
- $arrayContainersAnonymousContainer = @()
- $arrayContainersAnonymousBlob = @()
- $staticWebsitesState = 'n/a'
- $webSiteResponds = 'n/a'
-
- $subscriptionId = ($storageAccount.SA.id -split '/')[2]
- $resourceGroupName = ($storageAccount.SA.id -split '/')[4]
- $subDetails = $htAllSubscriptionsFromAPI.($subscriptionId).subDetails
-
- Write-Host "Processing Storage Account '$($storageAccount.SA.name)' - Subscription: '$($subDetails.displayName)' ($subscriptionId) [$($subDetails.subscriptionPolicies.quotaId)]"
-
- if ($storageAccount.SA.Properties.primaryEndpoints.blob) {
-
- $urlServiceProps = "$($storageAccount.SA.Properties.primaryEndpoints.blob)?restype=service&comp=properties"
- $saProperties = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $urlServiceProps -method 'GET' -listenOn 'Content' -currentTask "$($storageAccount.SA.name) get restype=service&comp=properties" -saResourceGroupName $resourceGroupName -unhandledErrorAction Continue
- if ($saProperties) {
- if ($saProperties -eq 'AuthorizationFailure' -or $saProperties -eq 'AuthorizationPermissionDenied' -or $saProperties -eq 'ResourceUnavailable' -or $saProperties -eq 'AuthorizationPermissionMismatch' ) {
- if ($saProperties -eq 'ResourceUnavailable') {
- $staticWebsitesState = $saProperties
- }
- }
- else {
- try {
- # ? https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/218#issuecomment-1854516882
- if ($saProperties.gettype().Name -eq 'Byte[]') {
- $byteArray = [byte[]]$saProperties
- $saProperties = [System.Text.Encoding]::UTF8.GetString($byteArray)
- }
-
- # $xmlSaProperties = [xml]([string]$saProperties -replace $saProperties.Substring(0, 3)) # Leading character:  (PS version <= 7.3.9)
- # $xmlSaProperties = [xml]([string]$saProperties -replace $saProperties.Substring(0, 1)) # Leading character: or U+feff (PS version >= 7.4.0)
- $xmlSaProperties = [xml]($saProperties -replace '^.*?<', '<') # Universal fix for all PS versions
-
- if ($xmlSaProperties.StorageServiceProperties.StaticWebsite) {
- if ($xmlSaProperties.StorageServiceProperties.StaticWebsite.Enabled -eq $true) {
- $staticWebsitesState = $true
- }
- else {
- $staticWebsitesState = $false
- }
- }
- }
- catch {
- Write-Host "XMLSAPropertiesFailed: Subscription: $($subDetails.displayName) ($subscriptionId) - Storage Account: $($storageAccount.SA.name)"
- Write-Host $($saProperties.ForEach({ [char]$_ }) -join '') -ForegroundColor Cyan
- }
- }
- }
-
- $urlCompList = "$($storageAccount.SA.Properties.primaryEndpoints.blob)?comp=list"
- $listContainers = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $urlCompList -method 'GET' -listenOn 'Content' -currentTask "$($storageAccount.SA.name) get comp=list" -unhandledErrorAction Continue
- if ($listContainers) {
- if ($listContainers -eq 'AuthorizationFailure' -or $listContainers -eq 'AuthorizationPermissionDenied' -or $listContainers -eq 'ResourceUnavailable' -or $listContainers -eq 'AuthorizationPermissionMismatch') {
- if ($listContainers -eq 'ResourceUnavailable') {
- $listContainersSuccess = $listContainers
- }
- else {
- $listContainersSuccess = $false
- }
- }
- else {
- $listContainersSuccess = $true
- }
-
- if ($listContainersSuccess -eq $true) {
- # ? https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/218#issuecomment-1854516882
- if ($listContainers.gettype().Name -eq 'Byte[]') {
- $byteArray = [byte[]]$listContainers
- $listContainers = [System.Text.Encoding]::UTF8.GetString($byteArray)
- }
-
- # $xmlListContainers = [xml]([string]$listContainers -replace $listContainers.Substring(0, 3)) # Leading character:  (PS version <= 7.3.9)
- # $xmlListContainers = [xml]([string]$listContainers -replace $listContainers.Substring(0, 1)) # Leading character: or U+feff (PS version >= 7.4.0)
- $xmlListContainers = [xml]($listContainers -replace '^.*?<', '<') # Universal fix for all PS versions
-
- $containersCount = $xmlListContainers.EnumerationResults.Containers.Container.Count
-
- foreach ($container in $xmlListContainers.EnumerationResults.Containers.Container) {
- $arrayContainers += $container.Name
- if ($container.Name -eq '$web' -and $staticWebsitesState) {
- if ($storageAccount.SA.properties.primaryEndpoints.web) {
- try {
- $testStaticWebsiteResponse = Invoke-WebRequest -Uri $storageAccount.SA.properties.primaryEndpoints.web -Method 'HEAD'
- $webSiteResponds = $true
- }
- catch {
- $webSiteResponds = $false
- }
- }
- }
-
- if ($container.Properties.PublicAccess) {
- if ($container.Properties.PublicAccess -eq 'blob') {
- $arrayContainersAnonymousBlob += $container.Name
- }
- if ($container.Properties.PublicAccess -eq 'container') {
- $arrayContainersAnonymousContainer += $container.Name
- }
- }
- }
- }
- }
- }
-
- $allowSharedKeyAccess = $storageAccount.SA.properties.allowSharedKeyAccess
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowSharedKeyAccess)) {
- $allowSharedKeyAccess = 'likely True'
- }
- $requireInfrastructureEncryption = $storageAccount.SA.properties.encryption.requireInfrastructureEncryption
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.encryption.requireInfrastructureEncryption)) {
- $requireInfrastructureEncryption = 'likely False'
- }
-
- $arrayResourceAccessRules = [System.Collections.ArrayList]@()
- if ($storageAccount.SA.properties.networkAcls.resourceAccessRules) {
- if ($storageAccount.SA.properties.networkAcls.resourceAccessRules.count -gt 0) {
- foreach ($resourceAccessRule in $storageAccount.SA.properties.networkAcls.resourceAccessRules) {
-
- $resourceAccessRuleResourceIdSplitted = $resourceAccessRule.resourceId -split '/'
- $resourceType = "$($resourceAccessRuleResourceIdSplitted[6])/$($resourceAccessRuleResourceIdSplitted[7])"
-
- [regex]$regex = '\*+'
- #$resourceAccessRule.resourceId
- switch ($regex.matches($resourceAccessRule.resourceId).count) {
- { $_ -eq 1 } {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'resourceGroup'
- sort = 3
- })
- }
- { $_ -eq 2 } {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'subscription'
- sort = 2
- })
- }
- { $_ -eq 3 } {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'tenant'
- sort = 1
- })
- }
- default {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'resource'
- resource = $resourceAccessRule.resourceId
- sort = 0
- })
- }
- }
- }
- }
- }
- $resourceAccessRulesCount = $arrayResourceAccessRules.count
- if ($resourceAccessRulesCount -eq 0) {
- $resourceAccessRules = ''
- }
- else {
- $ht = @{}
- foreach ($accessRulePerRange in $arrayResourceAccessRules | Group-Object -Property range | Sort-Object -Property Name -Descending) {
-
- if ($accessRulePerRange.Name -eq 'resource') {
- $arrayResources = @()
- foreach ($resource in $accessRulePerRange.Group.resource | Sort-Object) {
- $arrayResources += $resource
- }
- $ht.($accessRulePerRange.Name) = [array]($arrayResources)
- }
- else {
- $arrayResourceTypes = @()
- foreach ($resourceType in $accessRulePerRange.Group.resourceType | Sort-Object) {
- $arrayResourceTypes += $resourceType
- }
- $ht.($accessRulePerRange.Name) = [array]($arrayResourceTypes)
- }
- }
- $resourceAccessRules = $ht | ConvertTo-Json
- }
-
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.publicNetworkAccess)) {
- $publicNetworkAccess = 'likely Enabled'
- }
- else {
- $publicNetworkAccess = $storageAccount.SA.properties.publicNetworkAccess
- }
-
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowedCopyScope)) {
- $allowedCopyScope = 'From any Storage Account'
- }
- else {
- $allowedCopyScope = $storageAccount.SA.properties.allowedCopyScope
- }
-
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowCrossTenantReplication)) {
- if ($allowedCopyScope -ne 'From any Storage Account') {
- $allowCrossTenantReplication = "likely False (allowedCopyScope=$allowedCopyScope)"
- }
- else {
- $allowCrossTenantReplication = 'likely True'
- }
- }
- else {
- $allowCrossTenantReplication = $storageAccount.SA.properties.allowCrossTenantReplication
- }
-
- if ($storageAccount.SA.properties.dnsEndpointType) {
- $dnsEndpointType = $storageAccount.SA.properties.dnsEndpointType
- }
- else {
- $dnsEndpointType = 'standard'
- }
-
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- if ($htSACost.($storageAccount.SA.id)) {
- $hlpCost = $htSACost.($storageAccount.SA.id)
- $saCost = $hlpCost.costAll
- $saCostCurrency = $hlpCost.currencyAll
- $saCostMeterCategories = $hlpCost.meterCategoryAll
- }
- else {
- $saCost = 'n/a'
- $saCostCurrency = 'n/a'
- $saCostMeterCategories = 'n/a'
- }
- }
- else {
- $saCost = ''
- $saCostCurrency = ''
- $saCostMeterCategories = ''
- }
-
- $temp = [System.Collections.ArrayList]@()
- $null = $temp.Add([PSCustomObject]@{
- storageAccount = $storageAccount.SA.name
- kind = $storageAccount.SA.kind
- skuName = $storageAccount.SA.sku.name
- skuTier = $storageAccount.SA.sku.tier
- location = $storageAccount.SA.location
- creationTime = $storageAccount.SA.properties.creationTime
- allowBlobPublicAccess = $storageAccount.SA.properties.allowBlobPublicAccess
- publicNetworkAccess = $publicNetworkAccess
- SubscriptionId = $subscriptionId
- SubscriptionName = $subDetails.displayName
- subscriptionQuotaId = $subDetails.subscriptionPolicies.quotaId
- subscriptionMGPath = $htSubscriptionsMgPath.($subscriptionId).path -join '/'
- resourceGroup = $resourceGroupName
- networkAclsdefaultAction = $storageAccount.SA.properties.networkAcls.defaultAction
- staticWebsitesState = $staticWebsitesState
- staticWebsitesResponse = $webSiteResponds
- containersCanBeListed = $listContainersSuccess
- containersCount = $containersCount
- containers = $arrayContainers -join "$CSVDelimiterOpposite "
- containersAnonymousContainerCount = $arrayContainersAnonymousContainer.Count
- containersAnonymousContainer = $arrayContainersAnonymousContainer -join "$CSVDelimiterOpposite "
- containersAnonymousBlobCount = $arrayContainersAnonymousBlob.Count
- containersAnonymousBlob = $arrayContainersAnonymousBlob -join "$CSVDelimiterOpposite "
- ipRulesCount = $storageAccount.SA.properties.networkAcls.ipRules.Count
- ipRulesIPAddressList = ($storageAccount.SA.properties.networkAcls.ipRules.value | Sort-Object) -join "$CSVDelimiterOpposite "
- virtualNetworkRulesCount = $storageAccount.SA.properties.networkAcls.virtualNetworkRules.Count
- virtualNetworkRulesList = ($storageAccount.SA.properties.networkAcls.virtualNetworkRules.Id | Sort-Object) -join "$CSVDelimiterOpposite "
- resourceAccessRulesCount = $resourceAccessRulesCount
- resourceAccessRules = $resourceAccessRules
- bypass = ($storageAccount.SA.properties.networkAcls.bypass | Sort-Object) -join "$CSVDelimiterOpposite "
- supportsHttpsTrafficOnly = $storageAccount.SA.properties.supportsHttpsTrafficOnly
- minimumTlsVersion = $storageAccount.SA.properties.minimumTlsVersion
- allowSharedKeyAccess = $allowSharedKeyAccess
- requireInfrastructureEncryption = $requireInfrastructureEncryption
- allowedCopyScope = $allowedCopyScope
- allowCrossTenantReplication = $allowCrossTenantReplication
- dnsEndpointType = $dnsEndpointType
- usedCapacity = $storageAccount.SAUsedCapacity
- cost = $saCost
- metercategory = $saCostMeterCategories
- curreny = $saCostCurrency
- })
-
- if ($StorageAccountAccessAnalysisSubscriptionTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisSubscriptionTags.Count -gt 0) {
- foreach ($subTag4StorageAccountAccessAnalysis in $StorageAccountAccessAnalysisSubscriptionTags) {
- if ($htSubscriptionTags.($subscriptionId).$subTag4StorageAccountAccessAnalysis) {
- $temp | Add-Member -NotePropertyName "SubTag_$subTag4StorageAccountAccessAnalysis" -NotePropertyValue $($htSubscriptionTags.($subscriptionId).$subTag4StorageAccountAccessAnalysis)
- }
- else {
- $temp | Add-Member -NotePropertyName "SubTag_$subTag4StorageAccountAccessAnalysis" -NotePropertyValue 'n/a'
- }
- }
- }
-
- if ($StorageAccountAccessAnalysisStorageAccountTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisStorageAccountTags.Count -gt 0) {
- if ($storageAccount.SA.tags) {
- $htAllSATags = @{}
- foreach ($saTagName in ($storageAccount.SA.tags | Get-Member).where({ $_.MemberType -eq 'NoteProperty' }).Name) {
- $htAllSATags.$saTagName = $storageAccount.SA.tags.$saTagName
- }
- }
- foreach ($saTag4StorageAccountAccessAnalysis in $StorageAccountAccessAnalysisStorageAccountTags) {
- if ($htAllSATags.$saTag4StorageAccountAccessAnalysis) {
- $temp | Add-Member -NotePropertyName "SATag_$saTag4StorageAccountAccessAnalysis" -NotePropertyValue $($htAllSATags.$saTag4StorageAccountAccessAnalysis)
- }
- else {
- $temp | Add-Member -NotePropertyName "SATag_$saTag4StorageAccountAccessAnalysis" -NotePropertyValue 'n/a'
- }
- }
- }
-
- $null = $script:arrayStorageAccountAnalysisResults.AddRange($temp)
-
- } -ThrottleLimit $ThrottleLimit
- }
- else {
- Write-Host ' No Storage Accounts present'
- }
-
- $end = Get-Date
- Write-Host " Processing Storage Account Analysis duration: $((New-TimeSpan -Start $start -End $end).TotalMinutes) minutes ($((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds)"
-}
-function processTenantSummary() {
- Write-Host ' Building TenantSummary'
- showMemoryUsage
- if ($getMgParentName -eq 'Tenant Root') {
- $scopeNamingSummary = 'Tenant wide'
- }
- else {
- $scopeNamingSummary = "ManagementGroup '$ManagementGroupId' and descendants wide"
- }
-
- #region tenantSummaryPre
- $startRoleAssignmentsAllPre = Get-Date
- $roleAssignmentsallCount = ($rbacBaseQuery).count
- Write-Host " processing (pre) TenantSummary RoleAssignments (all $roleAssignmentsallCount)"
-
- #region RelatedPolicyAssignments
- $startRelatedPolicyAssignmentsAll = Get-Date
- $htRoleAssignmentRelatedPolicyAssignments = @{}
- $htOrphanedSPMI = @{}
- foreach ($roleAssignmentIdUnique in $roleAssignmentsUniqueById) {
-
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId) = @{}
-
- if ($htManagedIdentityForPolicyAssignment.($roleAssignmentIdUnique.RoleAssignmentIdentityObjectId)) {
- $hlpPolicyAssignmentId = ($htManagedIdentityForPolicyAssignment.($roleAssignmentIdUnique.RoleAssignmentIdentityObjectId).policyAssignmentId).ToLower()
- if (-not $htCacheAssignmentsPolicy.($hlpPolicyAssignmentId)) {
- if ($ManagementGroupId -eq $azAPICallConf['checkContext'].Tenant.Id) {
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- if (-not ($htCacheAssignmentsPolicyOnResourceGroupsAndResources).($hlpPolicyAssignmentId)) {
- Write-Host " !Relict detected: SP MI: $($roleAssignmentIdUnique.RoleAssignmentIdentityObjectId) - PolicyAssignmentId: $hlpPolicyAssignmentId"
- if (-not $htOrphanedSPMI.($roleAssignmentIdUnique.RoleAssignmentIdentityObjectId)) {
- $htOrphanedSPMI.($roleAssignmentIdUnique.RoleAssignmentIdentityObjectId) = @{}
- }
- }
- }
- else {
- Write-Host " !Relict detected: SP MI: $($roleAssignmentIdUnique.RoleAssignmentIdentityObjectId) - PolicyAssignmentId: $hlpPolicyAssignmentId"
- if (-not $htOrphanedSPMI.($roleAssignmentIdUnique.RoleAssignmentIdentityObjectId)) {
- $htOrphanedSPMI.($roleAssignmentIdUnique.RoleAssignmentIdentityObjectId) = @{}
- }
- }
- }
- }
- else {
- $temp0000000000 = $htCacheAssignmentsPolicy.($hlpPolicyAssignmentId)
- $policyAssignmentId = ($temp0000000000.Assignment.id).Tolower()
- $policyDefinitionId = ($temp0000000000.Assignment.properties.policyDefinitionId).Tolower()
-
-
- #builtin
- if ($policyDefinitionId -like '/providers/Microsoft.Authorization/policy*') {
- #policy
- if ($policyDefinitionId -like '/providers/Microsoft.Authorization/policyDefinitions/*') {
- $LinkOrNotLinkToAzAdvertizer = ($htCacheDefinitionsPolicy).($policyDefinitionId).LinkToAzAdvertizer
- }
- #policySet
- if ($policyDefinitionId -like '/providers/Microsoft.Authorization/policySetDefinitions/*') {
- $LinkOrNotLinkToAzAdvertizer = ($htCacheDefinitionsPolicySet).($policyDefinitionId).LinkToAzAdvertizer
- }
- }
- else {
- #policy
- if ($policyDefinitionId -like '*/providers/Microsoft.Authorization/policyDefinitions/*') {
- $policyDisplayName = ($htCacheDefinitionsPolicy).($policyDefinitionId).DisplayName
-
- }
- #policySet
- if ($policyDefinitionId -like '*/providers/Microsoft.Authorization/policySetDefinitions/*') {
- $policyDisplayName = ($htCacheDefinitionsPolicySet).($policyDefinitionId).DisplayName
-
- }
-
- $LinkOrNotLinkToAzAdvertizer = "$($policyDisplayName -replace '<', '<' -replace '>', '>') "
- }
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId).relatedPolicyAssignment = "$($policyAssignmentId) ($LinkOrNotLinkToAzAdvertizer)"
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId).relatedPolicyAssignmentClear = "$($policyAssignmentId) ($policyDisplayName)"
- }
- }
- else {
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId).relatedPolicyAssignment = 'none'
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId).relatedPolicyAssignmentClear = 'none'
- }
-
- if ($roleAssignmentIdUnique.RoleIsCustom -eq 'FALSE') {
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId).roleType = 'Builtin'
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId).roleWithWithoutLinkToAzAdvertizer = ($htCacheDefinitionsRole).($roleAssignmentIdUnique.RoleDefinitionId).LinkToAzAdvertizer
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId).roleClear = $roleAssignmentIdUnique.RoleDefinitionName
- }
- else {
-
- if ($roleAssigned.RoleSecurityCustomRoleOwner -eq 1) {
- $roletype = " Custom "
- }
- else {
- $roleType = 'Custom'
- }
-
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId).roleType = $roleType
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId).roleWithWithoutLinkToAzAdvertizer = $roleAssignmentIdUnique.RoleDefinitionName
- $htRoleAssignmentRelatedPolicyAssignments.($roleAssignmentIdUnique.RoleAssignmentId).roleClear = $roleAssignmentIdUnique.RoleDefinitionName
- }
- }
- $endRelatedPolicyAssignmentsAll = Get-Date
- Write-Host " RelatedPolicyAssignmentsAll duration: $((New-TimeSpan -Start $startRelatedPolicyAssignmentsAll -End $endRelatedPolicyAssignmentsAll).TotalMinutes) minutes ($((New-TimeSpan -Start $startRelatedPolicyAssignmentsAll -End $endRelatedPolicyAssignmentsAll).TotalSeconds) seconds)"
- #endregion RelatedPolicyAssignments
-
- #region createRBACAll
- $cnter = 0
- $script:rbacAll = [System.Collections.ArrayList]@()
- $startCreateRBACAll = Get-Date
- foreach ($rbac in $rbacBaseQuery) {
- $cnter++
- if ($cnter % 1000 -eq 0) {
- $etappeRoleAssignmentsAll = Get-Date
- Write-Host " $cnter of $roleAssignmentsallCount RoleAssignments processed; $((New-TimeSpan -Start $startRoleAssignmentsAllPre -End $etappeRoleAssignmentsAll).TotalSeconds) seconds"
- }
- $scope = $null
-
- if ($rbac.RoleAssignmentPIM -eq 'true') {
- $pim = $true
- $pimAssignmentType = $rbac.RoleAssignmentPIMAssignmentType
- $pimSlotStart = [string]$($rbac.RoleAssignmentPIMSlotStart)
- $pimSlotEnd = [string]$($rbac.RoleAssignmentPIMSlotEnd)
- }
- else {
- $pim = $false
- $pimAssignmentType = ''
- $pimSlotStart = ''
- $pimSlotEnd = ''
- }
-
- if ($rbac.RoleAssignmentId -like '/providers/Microsoft.Management/managementGroups/*') {
- $scopeTenOrMgOrSubOrRGOrRes = 'Mg'
- if (-not [String]::IsNullOrEmpty($rbac.SubscriptionId)) {
- $scope = "inherited $($rbac.RoleAssignmentScopeName)"
- }
- else {
- if (($rbac.RoleAssignmentScopeName) -eq $rbac.MgId) {
- $scope = 'thisScope MG'
- }
- else {
- $scope = "inherited $($rbac.RoleAssignmentScopeName)"
- }
- }
- }
-
- if ($rbac.RoleAssignmentId -like '/subscriptions/*') {
- $scope = 'thisScope Sub'
- $scopeTenOrMgOrSubOrRGOrRes = 'Sub'
- }
-
- if ($rbac.RoleAssignmentId -like '/subscriptions/*/resourcegroups/*') {
- $scope = 'thisScope Sub RG'
- $scopeTenOrMgOrSubOrRGOrRes = 'RG'
- }
-
- if ($rbac.RoleAssignmentId -like '/subscriptions/*/resourcegroups/*/providers/*/providers/*') {
- $scope = 'thisScope Sub RG Res'
- $scopeTenOrMgOrSubOrRGOrRes = 'Res'
- }
-
- if ($rbac.RoleAssignmentId -like '/providers/Microsoft.Authorization/roleAssignments/*') {
- $scope = 'inherited Tenant'
- $scopeTenOrMgOrSubOrRGOrRes = 'Ten'
- }
-
- $objectTypeUserType = ''
- if ($rbac.RoleAssignmentIdentityObjectType -eq 'User') {
- if ($htUserTypesGuest.($rbac.RoleAssignmentIdentityObjectId)) {
- $objectTypeUserType = 'Guest'
- }
- else {
- $objectTypeUserType = 'Member'
- }
- }
-
- if (-not [string]::IsNullOrEmpty($rbac.RoleDataActions) -or -not [string]::IsNullOrEmpty($rbac.RoleNotDataActions)) {
- $roleManageData = 'true'
- }
- else {
- $roleManageData = 'false'
- }
-
- $hlpRoleAssignmentRelatedPolicyAssignments = $htRoleAssignmentRelatedPolicyAssignments.($rbac.RoleAssignmentId)
-
- if (-not $NoAADGroupsResolveMembers) {
- if ($rbac.RoleAssignmentIdentityObjectType -eq 'Group') {
-
- $grpHlpr = $htAADGroupsDetails.($rbac.RoleAssignmentIdentityObjectId)
- $null = $script:rbacAll.Add([PSCustomObject]@{
- Level = $rbac.Level
- RoleAssignmentId = $rbac.RoleAssignmentId
- RoleAssignmentPIMRelated = $pim
- RoleAssignmentPIMAssignmentType = $pimAssignmentType
- RoleAssignmentPIMAssignmentSlotStart = $pimSlotStart
- RoleAssignmentPIMAssignmentSlotEnd = $pimSlotEnd
- CreatedBy = $rbac.RoleAssignmentCreatedBy
- CreatedOn = $rbac.RoleAssignmentCreatedOn
- UpdatedBy = $rbac.RoleAssignmentUpdatedBy
- UpdatedOn = $rbac.RoleAssignmentUpdatedOn
- MgId = $rbac.MgId
- MgName = $rbac.MgName
- MgParentId = $rbac.MgParentId
- MgParentName = $rbac.MgParentName
- SubscriptionId = $rbac.SubscriptionId
- SubscriptionName = $rbac.Subscription
- Scope = $scope
- ScopeTenOrMgOrSubOrRGOrRes = $scopeTenOrMgOrSubOrRGOrRes
- RoleAssignmentScopeName = $rbac.RoleAssignmentScopeName
- RoleAssignmentScopeRG = $rbac.RoleAssignmentScopeRG
- RoleAssignmentScopeRes = $rbac.RoleAssignmentScopeRes
- Role = $hlpRoleAssignmentRelatedPolicyAssignments.roleWithWithoutLinkToAzAdvertizer
- RoleClear = $hlpRoleAssignmentRelatedPolicyAssignments.roleClear
- RoleId = $rbac.RoleDefinitionId
- RoleType = $hlpRoleAssignmentRelatedPolicyAssignments.roleType
- RoleDataRelated = $roleManageData
- AssignmentType = 'direct'
- AssignmentInheritFrom = ''
- GroupMembersCount = "$($grpHlpr.MembersAllCount) (Usr: $($grpHlpr.MembersUsersCount)$($CsvDelimiterOpposite) Grp: $($grpHlpr.MembersGroupsCount)$($CsvDelimiterOpposite) SP: $($grpHlpr.MembersServicePrincipalsCount))"
- ObjectDisplayName = $rbac.RoleAssignmentIdentityDisplayname
- ObjectSignInName = $rbac.RoleAssignmentIdentitySignInName
- ObjectId = $rbac.RoleAssignmentIdentityObjectId
- ObjectType = $rbac.RoleAssignmentIdentityObjectType
- RbacRelatedPolicyAssignment = $hlpRoleAssignmentRelatedPolicyAssignments.relatedPolicyAssignment
- RbacRelatedPolicyAssignmentClear = $hlpRoleAssignmentRelatedPolicyAssignments.relatedPolicyAssignmentClear
- RoleSecurityCustomRoleOwner = $rbac.RoleSecurityCustomRoleOwner
- RoleSecurityOwnerAssignmentSP = $rbac.RoleSecurityOwnerAssignmentSP
- RoleCanDoRoleAssignments = $rbac.RoleCanDoRoleAssignments
- })
-
-
- if ($grpHlpr.MembersAllCount -gt 0) {
-
- if ($htAADGroupsDetails.($rbac.RoleAssignmentIdentityObjectId).MembersAllCount -le $AADGroupMembersLimit) {
-
- foreach ($groupmember in $htAADGroupsDetails.($rbac.RoleAssignmentIdentityObjectId).MembersAll) {
- if ($groupmember.'@odata.type' -eq '#microsoft.graph.user') {
- if ($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData -eq $true) {
- $grpMemberDisplayName = 'scrubbed'
- $grpMemberSignInName = 'scrubbed'
- }
- else {
- $grpMemberDisplayName = $groupmember.displayName
- $grpMemberSignInName = $groupmember.userPrincipalName
- }
- $grpMemberId = $groupmember.Id
- $grpMemberType = 'User'
- $grpMemberUserType = ''
-
- if ($htUserTypesGuest.($grpMemberId)) {
- $grpMemberUserType = 'Guest'
- }
- else {
- $grpMemberUserType = 'Member'
- }
-
- $identityTypeFull = "$grpMemberType $grpMemberUserType"
- }
- if ($groupmember.'@odata.type' -eq '#microsoft.graph.group') {
- $grpMemberDisplayName = $groupmember.displayName
- $grpMemberSignInName = 'n/a'
- $grpMemberId = $groupmember.Id
- $grpMemberType = 'Group'
- $grpMemberUserType = ''
- $identityTypeFull = "$grpMemberType"
- }
- if ($groupmember.'@odata.type' -eq '#microsoft.graph.servicePrincipal') {
- $grpMemberDisplayName = $groupmember.appDisplayName
- $grpMemberSignInName = 'n/a'
- $grpMemberId = $groupmember.Id
- $grpMemberType = 'ServicePrincipal'
- $grpMemberUserType = ''
- $identityType = $htServicePrincipals.($grpMemberId).spTypeConcatinated
- $identityTypeFull = "$identityType"
- }
-
- $null = $script:rbacAll.Add([PSCustomObject]@{
- Level = $rbac.Level
- RoleAssignmentId = $rbac.RoleAssignmentId
- RoleAssignmentPIMRelated = $pim
- RoleAssignmentPIMAssignmentType = $pimAssignmentType
- RoleAssignmentPIMAssignmentSlotStart = $pimSlotStart
- RoleAssignmentPIMAssignmentSlotEnd = $pimSlotEnd
- CreatedBy = $rbac.RoleAssignmentCreatedBy
- CreatedOn = $rbac.RoleAssignmentCreatedOn
- UpdatedBy = $rbac.RoleAssignmentUpdatedBy
- UpdatedOn = $rbac.RoleAssignmentUpdatedOn
- MgId = $rbac.MgId
- MgName = $rbac.MgName
- MgParentId = $rbac.MgParentId
- MgParentName = $rbac.MgParentName
- SubscriptionId = $rbac.SubscriptionId
- SubscriptionName = $rbac.Subscription
- Scope = $scope
- ScopeTenOrMgOrSubOrRGOrRes = $scopeTenOrMgOrSubOrRGOrRes
- RoleAssignmentScopeName = $rbac.RoleAssignmentScopeName
- RoleAssignmentScopeRG = $rbac.RoleAssignmentScopeRG
- RoleAssignmentScopeRes = $rbac.RoleAssignmentScopeRes
- Role = $hlpRoleAssignmentRelatedPolicyAssignments.roleWithWithoutLinkToAzAdvertizer
- RoleClear = $hlpRoleAssignmentRelatedPolicyAssignments.roleClear
- RoleId = $rbac.RoleDefinitionId
- RoleType = $hlpRoleAssignmentRelatedPolicyAssignments.roleType
- RoleDataRelated = $roleManageData
- AssignmentType = 'indirect'
- AssignmentInheritFrom = "$($rbac.RoleAssignmentIdentityDisplayname) ($($rbac.RoleAssignmentIdentityObjectId))"
- GroupMembersCount = "$($grpHlpr.MembersAllCount) (Usr: $($grpHlpr.MembersUsersCount)$($CsvDelimiterOpposite) Grp: $($grpHlpr.MembersGroupsCount)$($CsvDelimiterOpposite) SP: $($grpHlpr.MembersServicePrincipalsCount))"
- ObjectDisplayName = $grpMemberDisplayName
- ObjectSignInName = $grpMemberSignInName
- ObjectId = $grpMemberId
- ObjectType = $identityTypeFull
- RbacRelatedPolicyAssignment = $hlpRoleAssignmentRelatedPolicyAssignments.relatedPolicyAssignment
- RbacRelatedPolicyAssignmentClear = $hlpRoleAssignmentRelatedPolicyAssignments.relatedPolicyAssignmentClear
- RoleSecurityCustomRoleOwner = $rbac.RoleSecurityCustomRoleOwner
- RoleSecurityOwnerAssignmentSP = $rbac.RoleSecurityOwnerAssignmentSP
- RoleCanDoRoleAssignments = $rbac.RoleCanDoRoleAssignments
- })
- }
- }
- else {
- $null = $script:rbacAll.Add([PSCustomObject]@{
- Level = $rbac.Level
- RoleAssignmentId = $rbac.RoleAssignmentId
- RoleAssignmentPIMRelated = $pim
- RoleAssignmentPIMAssignmentType = $pimAssignmentType
- RoleAssignmentPIMAssignmentSlotStart = $pimSlotStart
- RoleAssignmentPIMAssignmentSlotEnd = $pimSlotEnd
- CreatedBy = $rbac.RoleAssignmentCreatedBy
- CreatedOn = $rbac.RoleAssignmentCreatedOn
- UpdatedBy = $rbac.RoleAssignmentUpdatedBy
- UpdatedOn = $rbac.RoleAssignmentUpdatedOn
- MgId = $rbac.MgId
- MgName = $rbac.MgName
- MgParentId = $rbac.MgParentId
- MgParentName = $rbac.MgParentName
- SubscriptionId = $rbac.SubscriptionId
- SubscriptionName = $rbac.Subscription
- Scope = $scope
- ScopeTenOrMgOrSubOrRGOrRes = $scopeTenOrMgOrSubOrRGOrRes
- RoleAssignmentScopeName = $rbac.RoleAssignmentScopeName
- RoleAssignmentScopeRG = $rbac.RoleAssignmentScopeRG
- RoleAssignmentScopeRes = $rbac.RoleAssignmentScopeRes
- Role = $hlpRoleAssignmentRelatedPolicyAssignments.roleWithWithoutLinkToAzAdvertizer
- RoleClear = $hlpRoleAssignmentRelatedPolicyAssignments.roleClear
- RoleId = $rbac.RoleDefinitionId
- RoleType = $hlpRoleAssignmentRelatedPolicyAssignments.roleType
- RoleDataRelated = $roleManageData
- AssignmentType = 'indirect'
- AssignmentInheritFrom = "$($rbac.RoleAssignmentIdentityDisplayname) ($($rbac.RoleAssignmentIdentityObjectId))"
- GroupMembersCount = "$($grpHlpr.MembersAllCount) (Usr: $($grpHlpr.MembersUsersCount)$($CsvDelimiterOpposite) Grp: $($grpHlpr.MembersGroupsCount)$($CsvDelimiterOpposite) SP: $($grpHlpr.MembersServicePrincipalsCount))"
- ObjectDisplayName = "Azure Governance Visualizer:TooManyMembers ($($htAADGroupsDetails.($rbac.RoleAssignmentIdentityObjectId).MembersAllCount))"
- ObjectSignInName = "Azure Governance Visualizer:TooManyMembers ($($htAADGroupsDetails.($rbac.RoleAssignmentIdentityObjectId).MembersAllCount))"
- ObjectId = "Azure Governance Visualizer:TooManyMembers ($($htAADGroupsDetails.($rbac.RoleAssignmentIdentityObjectId).MembersAllCount))"
- ObjectType = 'unresolved'
- RbacRelatedPolicyAssignment = $hlpRoleAssignmentRelatedPolicyAssignments.relatedPolicyAssignment
- RbacRelatedPolicyAssignmentClear = $hlpRoleAssignmentRelatedPolicyAssignments.relatedPolicyAssignmentClear
- RoleSecurityCustomRoleOwner = $rbac.RoleSecurityCustomRoleOwner
- RoleSecurityOwnerAssignmentSP = $rbac.RoleSecurityOwnerAssignmentSP
- RoleCanDoRoleAssignments = $rbac.RoleCanDoRoleAssignments
- })
- }
- }
-
- }
- else {
-
- if ($rbac.RoleAssignmentIdentityObjectType -eq 'ServicePrincipal') {
- $identityType = $htServicePrincipals.($rbac.RoleAssignmentIdentityObjectId).spTypeConcatinated
- $identityTypeFull = $identityType
- }
- elseif ($rbac.RoleAssignmentIdentityObjectType -eq 'Unknown') {
- $identityTypeFull = 'Unknown'
- }
- else {
- #user
- $identityType = $rbac.RoleAssignmentIdentityObjectType
- $identityTypeFull = "$identityType $objectTypeUserType"
- }
-
- $null = $script:rbacAll.Add([PSCustomObject]@{
- Level = $rbac.Level
- RoleAssignmentId = $rbac.RoleAssignmentId
- RoleAssignmentPIMRelated = $pim
- RoleAssignmentPIMAssignmentType = $pimAssignmentType
- RoleAssignmentPIMAssignmentSlotStart = $pimSlotStart
- RoleAssignmentPIMAssignmentSlotEnd = $pimSlotEnd
- CreatedBy = $rbac.RoleAssignmentCreatedBy
- CreatedOn = $rbac.RoleAssignmentCreatedOn
- UpdatedBy = $rbac.RoleAssignmentUpdatedBy
- UpdatedOn = $rbac.RoleAssignmentUpdatedOn
- MgId = $rbac.MgId
- MgName = $rbac.MgName
- MgParentId = $rbac.MgParentId
- MgParentName = $rbac.MgParentName
- SubscriptionId = $rbac.SubscriptionId
- SubscriptionName = $rbac.Subscription
- Scope = $scope
- ScopeTenOrMgOrSubOrRGOrRes = $scopeTenOrMgOrSubOrRGOrRes
- RoleAssignmentScopeName = $rbac.RoleAssignmentScopeName
- RoleAssignmentScopeRG = $rbac.RoleAssignmentScopeRG
- RoleAssignmentScopeRes = $rbac.RoleAssignmentScopeRes
- Role = $hlpRoleAssignmentRelatedPolicyAssignments.roleWithWithoutLinkToAzAdvertizer
- RoleClear = $hlpRoleAssignmentRelatedPolicyAssignments.roleClear
- RoleId = $rbac.RoleDefinitionId
- RoleType = $hlpRoleAssignmentRelatedPolicyAssignments.roleType
- RoleDataRelated = $roleManageData
- AssignmentType = 'direct'
- AssignmentInheritFrom = ''
- GroupMembersCount = ''
- ObjectDisplayName = $rbac.RoleAssignmentIdentityDisplayname
- ObjectSignInName = $rbac.RoleAssignmentIdentitySignInName
- ObjectId = $rbac.RoleAssignmentIdentityObjectId
- ObjectType = $identityTypeFull
- RbacRelatedPolicyAssignment = $hlpRoleAssignmentRelatedPolicyAssignments.relatedPolicyAssignment
- RbacRelatedPolicyAssignmentClear = $hlpRoleAssignmentRelatedPolicyAssignments.relatedPolicyAssignmentClear
- RoleSecurityCustomRoleOwner = $rbac.RoleSecurityCustomRoleOwner
- RoleSecurityOwnerAssignmentSP = $rbac.RoleSecurityOwnerAssignmentSP
- RoleCanDoRoleAssignments = $rbac.RoleCanDoRoleAssignments
- })
- }
- }
- else {
-
- if ($rbac.RoleAssignmentIdentityObjectType -eq 'ServicePrincipal') {
- $identityType = $htServicePrincipals.($rbac.RoleAssignmentIdentityObjectId).spTypeConcatinated
- $identityTypeFull = $identityType
- }
- elseif ($rbac.RoleAssignmentIdentityObjectType -eq 'Unknown') {
- $identityTypeFull = 'Unknown'
- }
- elseif ($rbac.RoleAssignmentIdentityObjectType -eq 'Group') {
- $identityTypeFull = 'Group'
- }
- else {
- #user
- $identityType = $rbac.RoleAssignmentIdentityObjectType
- $identityTypeFull = "$identityType $objectTypeUserType"
- }
-
- #noaadgroupmemberresolve
- $null = $script:rbacAll.Add([PSCustomObject]@{
- Level = $rbac.Level
- RoleAssignmentId = $rbac.RoleAssignmentId
- RoleAssignmentPIMRelated = $pim
- RoleAssignmentPIMAssignmentType = $pimAssignmentType
- RoleAssignmentPIMAssignmentSlotStart = $pimSlotStart
- RoleAssignmentPIMAssignmentSlotEnd = $pimSlotEnd
- CreatedBy = $rbac.RoleAssignmentCreatedBy
- CreatedOn = $rbac.RoleAssignmentCreatedOn
- UpdatedBy = $rbac.RoleAssignmentUpdatedBy
- UpdatedOn = $rbac.RoleAssignmentUpdatedOn
- MgId = $rbac.MgId
- MgName = $rbac.MgName
- MgParentId = $rbac.MgParentId
- MgParentName = $rbac.MgParentName
- SubscriptionId = $rbac.SubscriptionId
- SubscriptionName = $rbac.Subscription
- Scope = $scope
- ScopeTenOrMgOrSubOrRGOrRes = $scopeTenOrMgOrSubOrRGOrRes
- RoleAssignmentScopeName = $rbac.RoleAssignmentScopeName
- RoleAssignmentScopeRG = $rbac.RoleAssignmentScopeRG
- RoleAssignmentScopeRes = $rbac.RoleAssignmentScopeRes
- Role = $hlpRoleAssignmentRelatedPolicyAssignments.roleWithWithoutLinkToAzAdvertizer
- RoleClear = $hlpRoleAssignmentRelatedPolicyAssignments.roleClear
- RoleId = $rbac.RoleDefinitionId
- RoleType = $hlpRoleAssignmentRelatedPolicyAssignments.roleType
- RoleDataRelated = $roleManageData
- AssignmentType = 'direct'
- AssignmentInheritFrom = ''
- GroupMembersCount = ''
- ObjectDisplayName = $rbac.RoleAssignmentIdentityDisplayname
- ObjectSignInName = $rbac.RoleAssignmentIdentitySignInName
- ObjectId = $rbac.RoleAssignmentIdentityObjectId
- ObjectType = $identityTypeFull
- RbacRelatedPolicyAssignment = $hlpRoleAssignmentRelatedPolicyAssignments.relatedPolicyAssignment
- RbacRelatedPolicyAssignmentClear = $hlpRoleAssignmentRelatedPolicyAssignments.relatedPolicyAssignmentClear
- RoleSecurityCustomRoleOwner = $rbac.RoleSecurityCustomRoleOwner
- RoleSecurityOwnerAssignmentSP = $rbac.RoleSecurityOwnerAssignmentSP
- RoleCanDoRoleAssignments = $rbac.RoleCanDoRoleAssignments
- })
- }
- }
- #endregion createRBACAll
-
- #region PIMEligible
- if (-not $NoPIMEligibility) {
- $startPIMEnrichment = Get-Date
- Write-Host ' Processing PIMEnrichment'
- $PIMEligibleEnriched = [System.Collections.ArrayList]@()
- #$tfCountCnt = 0
- foreach ($PIMEligible in $arrayPIMEligible) {
- #$tfCountCnt++
- if ($PIMEligible.RoleType -eq 'BuiltInRole') {
- $roleName = "$($PIMEligible.RoleName) "
- }
- else {
- $roleName = $PIMEligible.RoleName
- }
- $null = $PIMEligibleEnriched.Add([PSCustomObject]@{
- Scope = $PIMEligible.ScopeType
- ScopeId = $PIMEligible.ScopeId
- ScopeName = $PIMEligible.ScopeDisplayName
- ManagementGroupId = $PIMEligible.ManagementGroupId
- ManagementGroupDisplayName = $PIMEligible.ManagementGroupDisplayName
- SubscriptionId = $PIMEligible.SubscriptionId
- SubscriptionDisplayName = $PIMEligible.SubscriptionDisplayName
- MgPath = $PIMEligible.MgPath -join '/'
- MgLevel = $PIMEligible.MgLevel
- Role = $roleName
- RoleClear = $PIMEligible.RoleName
- RoleId = $PIMEligible.RoleId
- RoleIdGuid = $PIMEligible.RoleIdGuid
- RoleType = $PIMEligible.RoleType
- IdentityObjectId = $PIMEligible.IdentityObjectId
- IdentityDisplayName = $PIMEligible.IdentityDisplayName
- IdentitySignInName = $PIMEligible.IdentityPrincipalName
- IdentityType = $PIMEligible.IdentityType
- IdentityApplicability = 'direct'
- AppliesThrough = ''
- PIMEligibilityId = $PIMEligible.PIMId
- PIMEligibility = $PIMEligible.PIMInheritance
- PIMEligibilityInheritedFrom = $PIMEligible.PIMInheritedFrom
- PIMEligibilityInheritedFromClear = $PIMEligible.PIMInheritedFromClear
- PIMEligibilityStartDateTime = [string]$PIMEligible.PIMStartDateTime
- PIMEligibilityEndDateTime = [string]$PIMEligible.PIMEndDateTime
- })
-
- if (-not $NoAADGroupsResolveMembers) {
- if ($PIMEligible.IdentityType -eq 'Group') {
- if ($htAADGroupsDetails.($PIMEligible.IdentityObjectId)) {
- foreach ($groupMemberUser in $htAADGroupsDetails.($PIMEligible.IdentityObjectId).MembersUsers) {
- #$tfCountCnt++
- $null = $PIMEligibleEnriched.Add([PSCustomObject]@{
- Scope = $PIMEligible.ScopeType
- ScopeId = $PIMEligible.ScopeId
- ScopeName = $PIMEligible.ScopeDisplayName
- ManagementGroupId = $PIMEligible.ManagementGroupId
- ManagementGroupDisplayName = $PIMEligible.ManagementGroupDisplayName
- SubscriptionId = $PIMEligible.SubscriptionId
- SubscriptionDisplayName = $PIMEligible.SubscriptionDisplayName
- MgPath = $PIMEligible.MgPath -join '/'
- MgLevel = $PIMEligible.MgLevel
- Role = $roleName
- RoleClear = $PIMEligible.RoleName
- RoleId = $PIMEligible.RoleId
- RoleIdGuid = $PIMEligible.RoleIdGuid
- RoleType = $PIMEligible.RoleType
- IdentityObjectId = $groupMemberUser.id
- IdentityDisplayName = $groupMemberUser.displayName
- IdentitySignInName = $groupMemberUser.userPrincipalName
- IdentityType = "User $($groupMemberUser.userType)"
- IdentityApplicability = 'nested'
- AppliesThrough = "$($PIMEligible.IdentityDisplayName) ($($PIMEligible.IdentityObjectId))"
- PIMEligibilityId = $PIMEligible.PIMId
- PIMEligibility = $PIMEligible.PIMInheritance
- PIMEligibilityInheritedFrom = $PIMEligible.PIMInheritedFrom
- PIMEligibilityInheritedFromClear = $PIMEligible.PIMInheritedFromClear
- PIMEligibilityStartDateTime = [string]$PIMEligible.PIMStartDateTime
- PIMEligibilityEndDateTime = [string]$PIMEligible.PIMEndDateTime
- })
- }
- }
- else {
- Write-Host "!! Unexpected: Group $($PIMEligible.IdentityDisplayName) ($($PIMEligible.IdentityObjectId)) not found in `$htAADGroupsDetails - please report back!"
- }
- }
- }
- }
- $endPIMEnrichment = Get-Date
- Write-Host " PIMEnrichment duration: $((New-TimeSpan -Start $startPIMEnrichment -End $endPIMEnrichment).TotalMinutes) minutes ($((New-TimeSpan -Start $startPIMEnrichment -End $endPIMEnrichment).TotalSeconds) seconds)"
-
- if (-not $NoPIMEligibilityIntegrationRoleAssignmentsAll) {
- $startPIMEnrichmentToRBACAll = Get-Date
- Write-Host ' Processing PIMEnrichment to RBACAll'
- foreach ($PIMEligibleRoleAssignment in $PIMEligibleEnriched) {
- if ($PIMEligibleRoleAssignment.PIMEligibility -eq 'Inherited') {
- $scope = "inherited $($PIMEligibleRoleAssignment.PIMEligibilityInheritedFromClear)"
- }
- else {
- $scope = "thisScope $($PIMEligibleRoleAssignment.Scope)"
- }
-
- if (-not [string]::IsNullOrEmpty($htCacheDefinitionsRole.($PIMEligibleRoleAssignment.RoleId).RoleDataActions) -or -not [string]::IsNullOrEmpty($htCacheDefinitionsRole.($PIMEligibleRoleAssignment.RoleId).RoleNotDataActions)) {
- $roleManageData = 'true'
- }
- else {
- $roleManageData = 'false'
- }
-
- $roleCanDoRoleAssignments = $false
- if ($htCacheDefinitionsRole.($PIMEligibleRoleAssignment.RoleId).RoleCanDoRoleAssignments) {
- $roleCanDoRoleAssignments = 'true'
- }
-
- $null = $script:rbacAll.Add([PSCustomObject]@{
- Level = $PIMEligibleRoleAssignment.MgLevel
- RoleAssignmentId = ''
- RoleAssignmentPIMRelated = $true
- RoleAssignmentPIMAssignmentType = 'Eligible'
- RoleAssignmentPIMAssignmentSlotStart = $PIMEligibleRoleAssignment.PIMEligibilityStartDateTime
- RoleAssignmentPIMAssignmentSlotEnd = $PIMEligibleRoleAssignment.PIMEligibilityEndDateTime
- CreatedBy = ''
- CreatedOn = ''
- UpdatedBy = $rbac.RoleAssignmentUpdatedBy
- UpdatedOn = $rbac.RoleAssignmentUpdatedOn
- MgId = $PIMEligibleRoleAssignment.ManagementGroupId
- MgName = $PIMEligibleRoleAssignment.ManagementGroupDisplayName
- MgParentId = '' #check
- MgParentName = '' #check
- SubscriptionId = $PIMEligibleRoleAssignment.SubscriptionId
- SubscriptionName = $PIMEligibleRoleAssignment.SubscriptionDisplayName
- Scope = $scope
- ScopeTenOrMgOrSubOrRGOrRes = $PIMEligibleRoleAssignment.Scope
- RoleAssignmentScopeName = $PIMEligibleRoleAssignment.Scope
- RoleAssignmentScopeRG = ''
- RoleAssignmentScopeRes = ''
- Role = $PIMEligibleRoleAssignment.Role
- RoleClear = $PIMEligibleRoleAssignment.RoleClear
- RoleId = $PIMEligibleRoleAssignment.RoleIdGuid
- RoleType = $PIMEligibleRoleAssignment.RoleType
- RoleDataRelated = $roleManageData #check
- AssignmentType = $PIMEligibleRoleAssignment.IdentityApplicability
- AssignmentInheritFrom = $PIMEligibleRoleAssignment.AppliesThrough
- GroupMembersCount = ''
- ObjectDisplayName = $PIMEligibleRoleAssignment.IdentityDisplayName
- ObjectSignInName = $PIMEligibleRoleAssignment.IdentitySignInName
- ObjectId = $PIMEligibleRoleAssignment.IdentityObjectId
- ObjectType = $PIMEligibleRoleAssignment.IdentityType
- RbacRelatedPolicyAssignment = ''
- RbacRelatedPolicyAssignmentClear = ''
- RoleSecurityCustomRoleOwner = '' #check $rbac.RoleSecurityCustomRoleOwner
- RoleSecurityOwnerAssignmentSP = '' #check $rbac.RoleSecurityOwnerAssignmentSP
- RoleCanDoRoleAssignments = $roleCanDoRoleAssignments
- })
- }
- $endPIMEnrichmentToRBACAll = Get-Date
- Write-Host " PIMEnrichment to RBACAll duration: $((New-TimeSpan -Start $startPIMEnrichmentToRBACAll -End $endPIMEnrichmentToRBACAll).TotalMinutes) minutes ($((New-TimeSpan -Start $startPIMEnrichmentToRBACAll -End $endPIMEnrichmentToRBACAll).TotalSeconds) seconds)"
- }
- }
- #endregion PIMEligible
-
- Write-Host ' Processing unresoved Identities (createdBy)'
- $startUnResolvedIdentitiesCreatedBy = Get-Date
- #prep prepUnresoledIdentities
- #region identitiesThatCreatedRoleAssignmentsButDontHaveARoleAssignmentThemselve
- $script:htIdentitiesWithRoleAssignmentsUnique = @{}
- $identitiesWithRoleAssignmentsUnique = $rbacAll.where( { $_.ObjectType -ne 'Unknown' } ) | Sort-Object -Property ObjectId -Unique | Select-Object ObjectType, ObjectDisplayName, ObjectSignInName, ObjectId
- foreach ($identityWithRoleAssignment in $identitiesWithRoleAssignmentsUnique | Sort-Object -Property objectType) {
-
- if (-not $htIdentitiesWithRoleAssignmentsUnique.($identityWithRoleAssignment.ObjectId)) {
- $script:htIdentitiesWithRoleAssignmentsUnique.($identityWithRoleAssignment.ObjectId) = @{}
-
- $arr = @()
- $ht = [ordered]@{}
- $identityWithRoleAssignment.psobject.properties | ForEach-Object {
- if ($_.Value) {
- $value = $_.Value
- }
- else {
- $value = 'n/a'
- }
- $arr += "$($_.Name): $value"
- $ht.($_.Name) = $value
- }
-
- $script:htIdentitiesWithRoleAssignmentsUnique.($identityWithRoleAssignment.ObjectId).details = $arr -join "$CsvDelimiterOpposite "
- $script:htIdentitiesWithRoleAssignmentsUnique.($identityWithRoleAssignment.ObjectId).detailsJson = $ht
- }
- }
- #endregion identitiesThatCreatedRoleAssignmentsButDontHaveARoleAssignmentThemselve
-
- #enrich rbacAll with createdBy and UpdatedBy identity information
- #region enrichrbacAll
- $htNonResolvedIdentities = @{}
- foreach ($rbac in $rbacAll) {
- $createdBy = $rbac.createdBy
- if (-not [string]::IsNullOrEmpty($createdBy)) {
- if ($htIdentitiesWithRoleAssignmentsUnique.($createdBy)) {
- $createdBy = $htIdentitiesWithRoleAssignmentsUnique.($createdBy).details
- $rbac.CreatedBy = $createdBy
- }
- else {
- if (-not $htNonResolvedIdentities.($rbac.createdBy)) {
- $htNonResolvedIdentities.($rbac.createdBy) = @{}
- }
- }
- }
-
- $updatedBy = $rbac.updatedBy
- if (-not [string]::IsNullOrEmpty($updatedBy)) {
- if ($htIdentitiesWithRoleAssignmentsUnique.($updatedBy)) {
- $updatedBy = $htIdentitiesWithRoleAssignmentsUnique.($updatedBy).details
- $rbac.UpdatedBy = $updatedBy
- }
- else {
- if (-not $htNonResolvedIdentities.($rbac.updatedBy)) {
- $htNonResolvedIdentities.($rbac.updatedBy) = @{}
- }
- }
- }
- }
- #endregion enrichrbacAll
-
- #region nonResolvedIdentities
- $htNonResolvedIdentitiesCount = $htNonResolvedIdentities.Count
- if ($htNonResolvedIdentitiesCount -gt 0) {
- Write-Host " $htNonResolvedIdentitiesCount unresolved identities that created a RBAC Role assignment (createdBy)"
- $arrayUnresolvedIdentities = @()
- $arrayUnresolvedIdentities = foreach ($unresolvedIdentity in $htNonResolvedIdentities.keys) {
- if (-not [string]::IsNullOrEmpty($unresolvedIdentity)) {
- $unresolvedIdentity
- }
- }
- $arrayUnresolvedIdentitiesCount = $arrayUnresolvedIdentities.Count
- Write-Host " $arrayUnresolvedIdentitiesCount unresolved identities that have a value"
- if ($arrayUnresolvedIdentitiesCount -gt 0) {
-
- $counterBatch = [PSCustomObject] @{ Value = 0 }
- $batchSize = 1000
- $ObjectBatch = $arrayUnresolvedIdentities | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
- $ObjectBatchCount = ($ObjectBatch | Measure-Object).Count
- $batchCnt = 0
-
- $script:htResolvedIdentities = @{}
-
- foreach ($batch in $ObjectBatch) {
- $batchCnt++
-
- $nonResolvedIdentitiesToCheck = '"{0}"' -f ($batch.Group.where({ testGuid $_ }) -join '","')
- Write-Host " IdentitiesToCheck: Batch #$batchCnt/$($ObjectBatchCount) ($(($batch.Group).Count))"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/directoryObjects/getByIds"
- $method = 'POST'
- $body = @"
- {
- "ids":[$($nonResolvedIdentitiesToCheck)]
- }
-"@
-
- function resolveIdentitiesRBAC($currentTask) {
- $resolvedIdentities = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -currentTask $currentTask
- $resolvedIdentitiesCount = $resolvedIdentities.Count
- Write-Host " $resolvedIdentitiesCount identities resolved"
- if ($resolvedIdentitiesCount -gt 0) {
-
- foreach ($resolvedIdentity in $resolvedIdentities) {
-
- if (-not $htResolvedIdentities.($resolvedIdentity.id)) {
-
- $script:htResolvedIdentities.($resolvedIdentity.id) = @{}
- if ($resolvedIdentity.'@odata.type' -eq '#microsoft.graph.servicePrincipal' -or $resolvedIdentity.'@odata.type' -eq '#microsoft.graph.user') {
- if ($resolvedIdentity.'@odata.type' -eq '#microsoft.graph.servicePrincipal') {
- if ($resolvedIdentity.servicePrincipalType -eq 'ManagedIdentity') {
- $miType = 'unknown'
- foreach ($altName in $resolvedIdentity.alternativeNames) {
- if ($altName -like 'isExplicit=*') {
- $splitAltName = $altName.split('=')
- if ($splitAltName[1] -eq 'true') {
- $miType = 'Usr'
- }
- if ($splitAltName[1] -eq 'false') {
- $miType = 'Sys'
- }
- }
- }
- $sptype = "MI $miType"
- $custObjectType = "ObjectType: SP $sptype, ObjectDisplayName: $($resolvedIdentity.displayName), ObjectSignInName: n/a, ObjectId: $($resolvedIdentity.id) (r)"
- $ht = @{}
- $ht.'ObjectType' = "SP $sptype"
- $ht.'ObjectDisplayName' = $($resolvedIdentity.displayName)
- $ht.'ObjectSignInName' = 'n/a'
- $ht.'ObjectId' = $resolvedIdentity.id
- }
- else {
- if ($resolvedIdentity.servicePrincipalType -eq 'Application') {
- $sptype = 'App'
- if ($resolvedIdentity.appOwnerOrganizationId -eq $azAPICallConf['checkContext'].Tenant.Id) {
- $custObjectType = "ObjectType: SP $sptype INT, ObjectDisplayName: $($resolvedIdentity.displayName), ObjectSignInName: n/a, ObjectId: $($resolvedIdentity.id) (r)"
- $ht = @{}
- $ht.'ObjectType' = "SP $sptype INT"
- $ht.'ObjectDisplayName' = $($resolvedIdentity.displayName)
- $ht.'ObjectSignInName' = 'n/a'
- $ht.'ObjectId' = $resolvedIdentity.id
- }
- else {
- $custObjectType = "ObjectType: SP $sptype EXT, ObjectDisplayName: $($resolvedIdentity.displayName), ObjectSignInName: n/a, ObjectId: $($resolvedIdentity.id) (r)"
- $ht = @{}
- $ht.'ObjectType' = "SP $sptype EXT"
- $ht.'ObjectDisplayName' = $($resolvedIdentity.displayName)
- $ht.'ObjectSignInName' = 'n/a'
- $ht.'ObjectId' = $resolvedIdentity.id
- }
- }
- else {
- Write-Host "* * * Unexpected IdentityType $($resolvedIdentity.servicePrincipalType)"
- }
- }
- $script:htResolvedIdentities.($resolvedIdentity.id).custObjectType = $custObjectType
- $script:htResolvedIdentities.($resolvedIdentity.id).obj = $resolvedIdentity
- }
-
- if ($resolvedIdentity.'@odata.type' -eq '#microsoft.graph.user') {
- if ($htParamteters.DoNotShowRoleAssignmentsUserData) {
- $hlpObjectDisplayName = 'scrubbed'
- $hlpObjectSigninName = 'scrubbed'
- }
- else {
- $hlpObjectDisplayName = $resolvedIdentity.displayName
- $hlpObjectSigninName = $resolvedIdentity.userPrincipalName
- }
- $custObjectType = "ObjectType: User, ObjectDisplayName: $hlpObjectDisplayName, ObjectSignInName: $hlpObjectSigninName, ObjectId: $($resolvedIdentity.id) (r)"
- $ht = @{}
- $ht.'ObjectType' = 'User'
- $ht.'ObjectDisplayName' = $hlpObjectDisplayName
- $ht.'ObjectSignInName' = $hlpObjectSigninName
- $ht.'ObjectId' = $resolvedIdentity.id
-
- $script:htResolvedIdentities.($resolvedIdentity.id).custObjectType = $custObjectType
- $script:htResolvedIdentities.($resolvedIdentity.id).obj = $resolvedIdentity
- }
- if (-not $htIdentitiesWithRoleAssignmentsUnique.($resolvedIdentity.id)) {
- $script:htIdentitiesWithRoleAssignmentsUnique.($resolvedIdentity.id) = @{}
- $script:htIdentitiesWithRoleAssignmentsUnique.($resolvedIdentity.id).details = $custObjectType
- $script:htIdentitiesWithRoleAssignmentsUnique.($resolvedIdentity.id).detailsJson = $ht
- }
- }
-
- if ($resolvedIdentity.'@odata.type' -ne '#microsoft.graph.user' -and $resolvedIdentity.'@odata.type' -ne '#microsoft.graph.servicePrincipal') {
- Write-Host "!!! * * * IdentityType '$($resolvedIdentity.'@odata.type')' was not considered by Azure Governance Visualizer - if you see this line, please file an issue on GitHub - thank you." -ForegroundColor Yellow
- }
- }
- }
- }
- }
- resolveIdentitiesRBAC -currentTask ' resolveObjectbyId RoleAssignment'
- }
-
- foreach ($rbac in $rbacAll.where( { $_.CreatedBy -notlike 'ObjectType*' -or $_.UpdatedBy -notlike 'ObjectType*' })) {
- if ($rbac.CreatedBy -notlike 'ObjectType*') {
- if ($htResolvedIdentities.($rbac.CreatedBy)) {
- $rbac.CreatedBy = $htResolvedIdentities.($rbac.CreatedBy).custObjectType
- }
- else {
- if ($rbac.RoleAssignmentPIMAssignmentType -eq 'Eligible') {
- $rbac.CreatedBy = ''
- }
- else {
- if ([string]::IsNullOrEmpty($rbac.CreatedBy)) {
- $rbac.CreatedBy = 'IsNullOrEmpty'
- }
- else {
- $rbac.CreatedBy = "$($rbac.CreatedBy)"
- }
- }
- }
- }
- if ($rbac.UpdatedBy -notlike 'ObjectType*') {
- if ($htResolvedIdentities.($rbac.UpdatedBy)) {
- $rbac.UpdatedBy = $htResolvedIdentities.($rbac.UpdatedBy).custObjectType
- }
- else {
- if ($rbac.RoleAssignmentPIMAssignmentType -eq 'Eligible') {
- $rbac.UpdatedBy = ''
- }
- else {
- if ([string]::IsNullOrEmpty($rbac.UpdatedBy)) {
- $rbac.UpdatedBy = 'IsNullOrEmpty'
- }
- else {
- $rbac.UpdatedBy = "$($rbac.UpdatedBy)"
- }
- }
- }
- }
- }
- }
- }
- $endUnResolvedIdentitiesCreatedBy = Get-Date
- Write-Host " UnresolvedIdentities (createdBy) duration: $((New-TimeSpan -Start $startUnResolvedIdentitiesCreatedBy -End $endUnResolvedIdentitiesCreatedBy).TotalMinutes) minutes ($((New-TimeSpan -Start $startUnResolvedIdentitiesCreatedBy -End $endUnResolvedIdentitiesCreatedBy).TotalSeconds) seconds)"
- #endregion nonResolvedIdentities
-
- $startRBACAllGrouping = Get-Date
- $script:rbacAllGroupedBySubscription = $rbacAll | Group-Object -Property SubscriptionId
- $script:rbacAllGroupedByManagementGroup = $rbacAll | Group-Object -Property MgId
- $endRBACAllGrouping = Get-Date
- Write-Host " RBACAll Grouping duration: $((New-TimeSpan -Start $startRBACAllGrouping -End $endRBACAllGrouping).TotalMinutes) minutes ($((New-TimeSpan -Start $startRBACAllGrouping -End $endRBACAllGrouping).TotalSeconds) seconds)"
- $endCreateRBACAll = Get-Date
- Write-Host " CreateRBACAll duration: $((New-TimeSpan -Start $startCreateRBACAll -End $endCreateRBACAll).TotalMinutes) minutes ($((New-TimeSpan -Start $startCreateRBACAll -End $endCreateRBACAll).TotalSeconds) seconds)"
- #endregion tenantSummaryPre
-
- showMemoryUsage
-
- #region tenantSummaryPolicy
- $htmlTenantSummary = [System.Text.StringBuilder]::new()
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-
Anything which can help you learn Azure Policy GitHub
-'@)
-
- #region SUMMARYcustompolicies
- $startCustPolLoop = Get-Date
- Write-Host ' processing TenantSummary Custom Policy definitions'
-
- $script:customPoliciesDetailed = [System.Collections.ArrayList]@()
- $script:tenantPoliciesDetailed = [System.Collections.ArrayList]@()
- foreach ($tenantPolicy in (($htCacheDefinitionsPolicy).Values | Sort-Object @{Expression = { $_.DisplayName } }, @{Expression = { $_.PolicyDefinitionId } })) {
-
- #uniqueAssignments
- $policyUniqueAssignments = $null
- if ($htPolicyWithAssignmentsBase.($tenantPolicy.PolicyDefinitionId)) {
- $policyUniqueAssignments = $htPolicyWithAssignmentsBase.($tenantPolicy.PolicyDefinitionId).Assignments | Sort-Object
- $policyUniqueAssignmentsCount = ($policyUniqueAssignments).count
- }
- else {
- $policyUniqueAssignmentsCount = 0
- }
-
- $uniqueAssignments = $null
- if ($policyUniqueAssignmentsCount -gt 0) {
- $policyUniqueAssignmentsList = "($($policyUniqueAssignments -join "$CsvDelimiterOpposite "))"
- $uniqueAssignments = "$policyUniqueAssignmentsCount $policyUniqueAssignmentsList"
- }
- else {
- $uniqueAssignments = $policyUniqueAssignmentsCount
- }
-
- #PolicyUsedInPolicySet
- $usedInPolicySet4JSON = $null
- $usedInPolicySet = 0
- $usedInPolicySet4CSV = ''
- $usedInPolicySetCount = 0
- if (($htPoliciesUsedInPolicySets).($tenantPolicy.PolicyDefinitionId)) {
- $hlpPolicySetUsed = ($htPoliciesUsedInPolicySets).($tenantPolicy.PolicyDefinitionId)
- $usedInPolicySet4JSON = $hlpPolicySetUsed.PolicySetIdOnly | Sort-Object
- $usedInPolicySet = "$(($hlpPolicySetUsed.PolicySet | Sort-Object) -join "$CsvDelimiterOpposite ")"
- $usedInPolicySet4CSV = "$(($hlpPolicySetUsed.PolicySet4CSV | Sort-Object) -join "$CsvDelimiterOpposite ")"
- $usedInPolicySetCount = ($hlpPolicySetUsed.PolicySet).Count
- }
-
- #policyEffect
- if ($tenantPolicy.effectDefaultValue -ne 'n/a') {
- $effect = "Default: $($tenantPolicy.effectDefaultValue); Allowed: $($tenantPolicy.effectAllowedValue)"
- }
- elseif ($tenantPolicy.effectFixedValue -ne 'n/a') {
- $effect = "Fixed: $($tenantPolicy.effectFixedValue)"
- }
- else {
- $effect = 'n/a'
- }
-
- if (($tenantPolicy.RoleDefinitionIds) -ne 'n/a') {
- $policyRoleDefinitionsArray = @()
- $policyRoleDefinitionsArray = foreach ($roleDefinitionId in $tenantPolicy.RoleDefinitionIds | Sort-Object) {
- if (($htCacheDefinitionsRole).($roleDefinitionId -replace '.*/').LinkToAzAdvertizer) {
- ($htCacheDefinitionsRole).($roleDefinitionId -replace '.*/').LinkToAzAdvertizer
- }
- else {
- ($htCacheDefinitionsRole).($roleDefinitionId -replace '.*/').Name -replace '<', '<' -replace '>', '>'
- }
- }
- $policyRoleDefinitionsClearArray = @()
- $policyRoleDefinitionsClearArray = foreach ($roleDefinitionId in $tenantPolicy.RoleDefinitionIds | Sort-Object) {
- ($htCacheDefinitionsRole).($roleDefinitionId -replace '.*/').Name
- }
- $policyRoleDefinitions = $policyRoleDefinitionsArray -join "$CsvDelimiterOpposite "
- $policyRoleDefinitionsClear = $policyRoleDefinitionsClearArray -join "$CsvDelimiterOpposite "
- }
- else {
- $policyRoleDefinitions = 'n/a'
- $policyRoleDefinitionsClear = 'n/a'
- }
-
- # if ($tenantPolicy.Json.properties.metadata.version) {
- # $policyVersion = $tenantPolicy.Json.properties.metadata.version
- # }
- # else {
- # $policyVersion = 'n/a'
- # }
-
- if ($tenantPolicy.Type -eq 'Custom') {
-
- $createdOn = ''
- $createdBy = ''
- $createdByJson = ''
- $updatedOn = ''
- $updatedBy = ''
- $updatedByJson = ''
- if ($tenantPolicy.Json.properties.metadata.createdOn) {
- $createdOn = $tenantPolicy.Json.properties.metadata.createdOn
- }
- if ($tenantPolicy.Json.properties.metadata.createdBy) {
- $createdBy = $tenantPolicy.Json.properties.metadata.createdBy
- $createdByJson = $createdBy
- if ($createdBy -ne 'n/a') {
- if ($htIdentitiesWithRoleAssignmentsUnique.($createdBy)) {
- $createdByJson = $htIdentitiesWithRoleAssignmentsUnique.($createdBy).detailsJson
- $createdBy = $htIdentitiesWithRoleAssignmentsUnique.($createdBy).details
-
- }
- }
- }
- if ($tenantPolicy.Json.properties.metadata.updatedOn) {
- $updatedOn = $tenantPolicy.Json.properties.metadata.updatedOn
- }
- if ($tenantPolicy.Json.properties.metadata.updatedBy) {
- $updatedBy = $tenantPolicy.Json.properties.metadata.updatedBy
- $updatedByJson = $updatedBy
- if ($updatedBy -ne 'n/a') {
- if ($htIdentitiesWithRoleAssignmentsUnique.($updatedBy)) {
- $updatedByJson = $htIdentitiesWithRoleAssignmentsUnique.($updatedBy).detailsJson
- $updatedBy = $htIdentitiesWithRoleAssignmentsUnique.($updatedBy).details
-
- }
- }
- }
-
- $null = $script:customPoliciesDetailed.Add([PSCustomObject]@{
- Type = 'Custom'
- ScopeMGLevel = $tenantPolicy.ScopeMGLevel
- Scope = $tenantPolicy.ScopeMgSub
- ScopeId = $tenantPolicy.ScopeId
- PolicyDisplayName = $tenantPolicy.DisplayName
- PolicyDefinitionName = $tenantPolicy.Name
- PolicyDefinitionId = $tenantPolicy.PolicyDefinitionId
- PolicyVersion = $tenantPolicy.Version
- PolicyEffect = $effect
- PolicyCategory = $tenantPolicy.Category
- RoleDefinitions = $policyRoleDefinitions
- RoleDefinitionsClear = $policyRoleDefinitionsClear
- UniqueAssignments = $uniqueAssignments
- UsedInPolicySetsCount = $usedInPolicySetCount
- UsedInPolicySets = $usedInPolicySet
- UsedInPolicySet4CSV = $usedInPolicySet4CSV
- CreatedOn = $createdOn
- CreatedBy = $createdBy
- UpdatedOn = $updatedOn
- UpdatedBy = $updatedBy
- ALZ = $tenantPolicy.ALZ
- ALZState = $tenantPolicy.ALZState
- ALZLatestVer = $tenantPolicy.ALZLatestVer
- ALZIdentificationLevel = $tenantPolicy.ALZIdentificationLevel
- ALZPolicyName = $tenantPolicy.ALZPolicyName
- #Json = [string]($tenantPolicy.Json | ConvertTo-Json -Depth 99 -EnumsAsStrings)
- })
-
- $null = $script:tenantPoliciesDetailed.Add([PSCustomObject]@{
- Type = 'Custom'
- ScopeMGLevel = $tenantPolicy.ScopeMGLevel
- Scope = $tenantPolicy.ScopeMgSub
- ScopeId = $tenantPolicy.ScopeId
- PolicyDisplayName = $tenantPolicy.DisplayName
- PolicyDefinitionName = $tenantPolicy.Name
- PolicyDefinitionId = $tenantPolicy.PolicyDefinitionId
- PolicyVersion = $tenantPolicy.Version
- PolicyEffect = $effect
- PolicyCategory = $tenantPolicy.Category
- UniqueAssignmentsCount = $policyUniqueAssignmentsCount
- UniqueAssignments = $policyUniqueAssignments
- UsedInPolicySetsCount = $usedInPolicySetCount
- UsedInPolicySets = $usedInPolicySet
- UsedInPolicySet4CSV = $usedInPolicySet4CSV
- UsedInPolicySet4JSON = $usedInPolicySet4JSON
- CreatedOn = $createdOn
- CreatedBy = $createdBy
- CreatedByJson = $createdByJson
- UpdatedOn = $updatedOn
- UpdatedBy = $updatedBy
- UpdatedByJson = $updatedByJson
- #Json = [string]($tenantPolicy.Json | ConvertTo-Json -Depth 99 -EnumsAsStrings)
- Json = $tenantPolicy.Json
- ALZ = $tenantPolicy.ALZ
- ALZState = $tenantPolicy.ALZState
- ALZLatestVer = $tenantPolicy.ALZLatestVer
- ALZIdentificationLevel = $tenantPolicy.ALZIdentificationLevel
- ALZPolicyName = $tenantPolicy.ALZPolicyName
- })
- }
- else {
- $null = $script:tenantPoliciesDetailed.Add([PSCustomObject]@{
- Type = $tenantPolicy.Type
- ScopeMGLevel = $null
- Scope = $null
- ScopeId = $null
- PolicyDisplayName = $tenantPolicy.DisplayName
- PolicyDefinitionName = $tenantPolicy.Name
- PolicyDefinitionId = $tenantPolicy.PolicyDefinitionId
- PolicyVersion = $tenantPolicy.Version
- PolicyEffect = $effect
- PolicyCategory = $tenantPolicy.Category
- UniqueAssignmentsCount = $policyUniqueAssignmentsCount
- UniqueAssignments = $policyUniqueAssignments
- UsedInPolicySetsCount = $usedInPolicySetCount
- UsedInPolicySets = $usedInPolicySet
- UsedInPolicySet4CSV = $usedInPolicySet4CSV
- UsedInPolicySet4JSON = $usedInPolicySet4JSON
- CreatedOn = $null
- CreatedBy = $null
- CreatedByJson = $null
- UpdatedOn = $null
- UpdatedBy = $null
- UpdatedByJson = $null
- #Json = [string]($tenantPolicy.Json | ConvertTo-Json -Depth 99 -EnumsAsStrings)
- Json = $tenantPolicy.Json
- ALZ = $tenantPolicy.ALZ
- ALZState = $tenantPolicy.ALZState
- ALZLatestVer = $tenantPolicy.ALZLatestVer
- ALZIdentificationLevel = $tenantPolicy.ALZIdentificationLevel
- ALZPolicyName = $tenantPolicy.ALZPolicyName
- })
- }
- }
-
- if (-not $NoCsvExport) {
- $csvFilename = "$($filename)_PolicyDefinitions"
- Write-Host " Exporting PolicyDefinitions CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
- $tenantPoliciesDetailed | Sort-Object -Property Type, Scope, PolicyDefinitionId | Select-Object -ExcludeProperty UniqueAssignments, UsedInPolicySets, UsedInPolicySet4JSON, CreatedByJson, UpdatedByJson, Json | Export-Csv -Encoding utf8 -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter $csvDelimiter -NoTypeInformation
- }
-
- if ($getMgParentName -eq 'Tenant Root') {
-
- if ($tenantCustomPoliciesCount -gt 0) {
- $tfCount = $tenantCustomPoliciesCount
- $htmlTableId = 'TenantSummary_customPolicies'
- [void]$htmlTenantSummary.AppendLine(@"
-
$tenantCustomPoliciesCount Custom Policy definitions ($scopeNamingSummary)
-
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-Scope Id
-Policy DisplayName
-Policy Name
-PolicyId
-Category
-ALZ
-Effect
-Role definitions
-Unique assignments
-Used in PolicySets
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlSUMMARYcustompolicies = $null
- $htmlSUMMARYcustompolicies = foreach ($customPolicy in ($customPoliciesDetailed | Sort-Object @{Expression = { $_.PolicyDisplayName } }, @{Expression = { $_.PolicyDefinitionId } })) {
- if ($custompolicy.UsedInPolicySetsCount -gt 0) {
- $customPolicyUsedInPolicySets = "$($customPolicy.UsedInPolicySetsCount) ($($customPolicy.UsedInPolicySets))"
- }
- else {
- $customPolicyUsedInPolicySets = $($customPolicy.UsedInPolicySetsCount)
- }
- @"
-
-$($customPolicy.Scope)
-$($customPolicy.ScopeId)
-$($customPolicy.PolicyDisplayName -replace '<', '<' -replace '>', '>')
-$($customPolicy.PolicyDefinitionName -replace '<', '<' -replace '>', '>')
-$($customPolicy.PolicyDefinitionId -replace '<', '<' -replace '>', '>')
-$($customPolicy.PolicyCategory -replace '<', '<' -replace '>', '>')
-$($customPolicy.ALZ)
-$($customPolicy.PolicyEffect)
-$($customPolicy.RoleDefinitions)
-$($customPolicy.UniqueAssignments -replace '<', '<' -replace '>', '>')
-$($customPolicyUsedInPolicySets)
-$($customPolicy.CreatedOn)
-$($customPolicy.CreatedBy)
-$($customPolicy.UpdatedOn)
-$($customPolicy.UpdatedBy)
-
-"@
- }
-
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYcustompolicies)
- $htmlTenantSummary | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $htmlTenantSummary = [System.Text.StringBuilder]::new()
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$tenantCustomPoliciesCount Custom Policy definitions ($scopeNamingSummary)
-"@)
- }
- }
- #SUMMARY NOT tenant total custom policy definitions
- else {
- $faimage = "
"
-
- if ($tenantCustomPoliciesCount -gt 0) {
- $tfCount = $tenantCustomPoliciesCount
- $customPoliciesInScopeArray = [System.Collections.ArrayList]@()
- foreach ($customPolicy in ($tenantCustomPolicies | Sort-Object @{Expression = { $_.DisplayName } }, @{Expression = { $_.PolicyDefinitionId } })) {
- if (($customPolicy.PolicyDefinitionId) -like '/providers/Microsoft.Management/managementGroups/*') {
- $policyScopedMgSub = $customPolicy.PolicyDefinitionId -replace '/providers/Microsoft.Management/managementGroups/', '' -replace '/.*'
- if ($mgsAndSubs.MgId -contains ($policyScopedMgSub)) {
- $null = $customPoliciesInScopeArray.Add($customPolicy)
- }
- }
-
- if (($customPolicy.PolicyDefinitionId) -like '/subscriptions/*') {
- $policyScopedMgSub = $customPolicy.PolicyDefinitionId -replace '/subscriptions/', '' -replace '/.*'
- if ($mgsAndSubs.SubscriptionId -contains ($policyScopedMgSub)) {
- $null = $customPoliciesInScopeArray.Add($customPolicy)
- }
- else {
- #Write-Host "$policyScopedMgSub NOT in Scope"
- }
- }
- }
- $customPoliciesFromSuperiorMGs = $tenantCustomPoliciesCount - (($customPoliciesInScopeArray).count)
- }
- else {
- $customPoliciesFromSuperiorMGs = '0'
- }
-
- if ($tenantCustomPoliciesCount -gt 0) {
- $tfCount = $tenantCustomPoliciesCount
- $htmlTableId = 'TenantSummary_customPolicies'
- [void]$htmlTenantSummary.AppendLine(@"
-
$tenantCustomPoliciesCount Custom Policy definitions $scopeNamingSummary ($customPoliciesFromSuperiorMGs from superior scopes)
-
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-Scope Id
-Policy DisplayName
-Policy Name
-PolicyId
-Category
-ALZ
-Policy Effect
-Role definitions
-Unique assignments
-Used in PolicySets
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlSUMMARYcustompolicies = $null
- $htmlSUMMARYcustompolicies = foreach ($customPolicy in ($customPoliciesDetailed | Sort-Object @{Expression = { $_.PolicyDisplayName } }, @{Expression = { $_.PolicyDefinitionId } })) {
- if ($custompolicy.UsedInPolicySetsCount -gt 0) {
- $customPolicyUsedInPolicySets = "$($customPolicy.UsedInPolicySetsCount) ($($customPolicy.UsedInPolicySets))"
- }
- else {
- $customPolicyUsedInPolicySets = $($customPolicy.UsedInPolicySetsCount)
- }
- @"
-
-$($customPolicy.Scope)
-$($customPolicy.ScopeId)
-$($customPolicy.PolicyDisplayName -replace '<', '<' -replace '>', '>')
-$($customPolicy.PolicyDefinitionName -replace '<', '<' -replace '>', '>')
-$($customPolicy.PolicyDefinitionId -replace '<', '<' -replace '>', '>')
-$($customPolicy.PolicyCategory -replace '<', '<' -replace '>', '>')
-$($customPolicy.ALZ)
-$($customPolicy.PolicyEffect)
-$($customPolicy.RoleDefinitions)
-$($customPolicy.UniqueAssignments -replace '<', '<' -replace '>', '>')
-$($customPolicyUsedInPolicySets)
-$($customPolicy.CreatedOn)
-$($customPolicy.CreatedBy)
-$($customPolicy.UpdatedOn)
-$($customPolicy.UpdatedBy)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYcustompolicies)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$tenantCustomPoliciesCount Custom Policy definitions ($scopeNamingSummary)
-"@)
- }
- }
- $endCustPolLoop = Get-Date
- Write-Host " Custom Policy processing duration: $((New-TimeSpan -Start $startCustPolLoop -End $endCustPolLoop).TotalMinutes) minutes ($((New-TimeSpan -Start $startCustPolLoop -End $endCustPolLoop).TotalSeconds) seconds)"
- #endregion SUMMARYcustompolicies
-
- $startcustpolorph = Get-Date
- #region SUMMARYCustomPoliciesOrphandedTenantRoot
- Write-Host ' processing TenantSummary Custom Policy definitions orphaned'
- if ($getMgParentName -eq 'Tenant Root') {
- $customPoliciesOrphaned = [System.Collections.ArrayList]@()
- foreach ($customPolicyAll in $tenantCustomPolicies) {
- if (($policyPolicyBaseQueryUniqueCustomDefinitions).count -eq 0) {
- $null = $customPoliciesOrphaned.Add($customPolicyAll)
- }
- else {
- if ($policyPolicyBaseQueryUniqueCustomDefinitions -notcontains ($customPolicyAll.PolicyDefinitionId)) {
- $null = $customPoliciesOrphaned.Add($customPolicyAll)
- }
- }
- }
-
- $arrayCustomPoliciesOrphanedFinal = [System.Collections.ArrayList]@()
- foreach ($customPolicyOrphaned in $customPoliciesOrphaned) {
- if ($customPolicyOrphaned.Id) {
- if (-not $htPoliciesUsedInPolicySets.($customPolicyOrphaned.Id)) {
- $null = $arrayCustomPoliciesOrphanedFinal.Add($customPolicyOrphaned)
- }
- }
- else {
- Write-Host '!!!!!!!!!!!!!!!!!!!!! no Id'
- Write-Host '## all:'
- $customPoliciesOrphaned
- Write-Host '## customPolicyOrphaned no Id:'
- $customPolicyOrphaned
- }
- }
-
- #rgchange
- $arrayCustomPoliciesOrphanedFinalIncludingResourceGroups = [System.Collections.ArrayList]@()
- foreach ($customPolicyOrphanedFinal in $arrayCustomPoliciesOrphanedFinal) {
- if (($htCacheAssignmentsPolicyOnResourceGroupsAndResources).values.properties.PolicyDefinitionId -notcontains $customPolicyOrphanedFinal.PolicyDefinitionId) {
- $null = $arrayCustomPoliciesOrphanedFinalIncludingResourceGroups.Add($customPolicyOrphanedFinal)
- }
- }
-
- if (($arrayCustomPoliciesOrphanedFinalIncludingResourceGroups).count -gt 0) {
- $tfCount = ($arrayCustomPoliciesOrphanedFinalIncludingResourceGroups).count
- $htmlTableId = 'TenantSummary_customPoliciesOrphaned'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arrayCustomPoliciesOrphanedFinalIncludingResourceGroups).count) Orphaned Custom Policy definitions ($scopeNamingSummary)
-
-
Download CSV
semicolon |
comma
-
-
-
-Policy DisplayName
-PolicyId
-
-
-
-"@)
- $htmlSUMMARYCustomPoliciesOrphandedTenantRoot = $null
- $htmlSUMMARYCustomPoliciesOrphandedTenantRoot = foreach ($customPolicyOrphaned in $arrayCustomPoliciesOrphanedFinalIncludingResourceGroups | Sort-Object @{Expression = { $_.PolicyDefinitionId } }, @{Expression = { $_.DisplayName } }) {
- @"
-
-$($customPolicyOrphaned.DisplayName)
-$($customPolicyOrphaned.PolicyDefinitionId)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYCustomPoliciesOrphandedTenantRoot)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($customPoliciesOrphaned).count) Orphaned Custom Policy definitions ($scopeNamingSummary)
-"@)
- }
- }
- #SUMMARY Custom Policy definitions Orphanded NOT TenantRoot
- else {
- $customPoliciesOrphaned = [System.Collections.ArrayList]@()
- foreach ($customPolicyAll in $tenantCustomPolicies) {
- if (($policyPolicyBaseQueryUniqueCustomDefinitions).count -eq 0) {
- $null = $customPoliciesOrphaned.Add($customPolicyAll)
- }
- else {
- if ($policyPolicyBaseQueryUniqueCustomDefinitions -notcontains ($customPolicyAll.PolicyDefinitionId)) {
- $null = $customPoliciesOrphaned.Add($customPolicyAll)
- }
- }
- }
-
- $customPoliciesOrphanedInScopeArray = [System.Collections.ArrayList]@()
- foreach ($customPolicyOrphaned in $customPoliciesOrphaned) {
- $hlpOrphanedInScope = $customPolicyOrphaned
- if (($hlpOrphanedInScope.PolicyDefinitionId) -like '/providers/Microsoft.Management/managementGroups/*') {
- $policyScopedMgSub = $hlpOrphanedInScope.PolicyDefinitionId -replace '/providers/Microsoft.Management/managementGroups/' -replace '/.*'
- if ($mgsAndSubs.MgId -contains ($policyScopedMgSub)) {
- $null = $customPoliciesOrphanedInScopeArray.Add($hlpOrphanedInScope)
- }
- }
- if (($hlpOrphanedInScope.PolicyDefinitionId) -like '/subscriptions/*') {
- $policyScopedMgSub = $hlpOrphanedInScope.PolicyDefinitionId -replace '/subscriptions/' -replace '/.*'
- if ($mgsAndSubs.SubscriptionId -contains ($policyScopedMgSub)) {
- $null = $customPoliciesOrphanedInScopeArray.Add($hlpOrphanedInScope)
- }
- }
- }
-
- $arrayCustomPoliciesOrphanedFinal = [System.Collections.ArrayList]@()
- foreach ($customPolicyOrphanedInScopeArray in $customPoliciesOrphanedInScopeArray) {
- if (-not $htPoliciesUsedInPolicySets.($customPolicyOrphanedInScopeArray.Id)) {
- $null = $arrayCustomPoliciesOrphanedFinal.Add($customPolicyOrphanedInScopeArray)
- }
- }
-
- $arrayCustomPoliciesOrphanedFinalIncludingResourceGroups = [System.Collections.ArrayList]@()
- foreach ($customPolicyOrphanedFinal in $arrayCustomPoliciesOrphanedFinal) {
- if (($htCacheAssignmentsPolicyOnResourceGroupsAndResources).values.properties.PolicyDefinitionId -notcontains $customPolicyOrphanedFinal.PolicyDefinitionId) {
- $null = $arrayCustomPoliciesOrphanedFinalIncludingResourceGroups.Add($customPolicyOrphanedFinal)
- }
- }
-
- if (($arrayCustomPoliciesOrphanedFinalIncludingResourceGroups).count -gt 0) {
- $tfCount = ($arrayCustomPoliciesOrphanedFinalIncludingResourceGroups).count
- $htmlTableId = 'TenantSummary_customPoliciesOrphaned'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arrayCustomPoliciesOrphanedFinalIncludingResourceGroups).count) Orphaned Custom Policy definitions ($scopeNamingSummary)
-
-
Download CSV
semicolon |
comma
-
-
-
-Policy DisplayName
-PolicyId
-
-
-
-"@)
- $htmlSUMMARYCustomPoliciesOrphandedTenantRoot = $null
- $htmlSUMMARYCustomPoliciesOrphandedTenantRoot = foreach ($customPolicyOrphaned in $arrayCustomPoliciesOrphanedFinalIncludingResourceGroups | Sort-Object @{Expression = { $_.PolicyDefinitionId } }, @{Expression = { $_.DisplayName } }) {
- @"
-
-$($customPolicyOrphaned.DisplayName)
-$($customPolicyOrphaned.PolicyDefinitionId)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYCustomPoliciesOrphandedTenantRoot)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($arrayCustomPoliciesOrphanedFinalIncludingResourceGroups.count) Orphaned Custom Policy definitions ($scopeNamingSummary)
-"@)
- }
- }
- #endregion SUMMARYCustomPoliciesOrphandedTenantRoot
- $endcustpolorph = Get-Date
- Write-Host " processing TenantSummary Custom Policy definitions orphaned duration: $((New-TimeSpan -Start $startcustpolorph -End $endcustpolorph).TotalSeconds) seconds"
-
- #region SUMMARYtenanttotalcustompolicySets
- $startCustPolSetLoop = Get-Date
- Write-Host ' processing TenantSummary Custom PolicySet definitions'
- $script:customPolicySetsDetailed = [System.Collections.ArrayList]@()
- $script:tenantPolicySetsDetailed = [System.Collections.ArrayList]@()
- $custompolicySetsInScopeArray = [System.Collections.ArrayList]@()
- foreach ($tenantPolicySet in ($tenantAllPolicySets)) {
-
- $policySetUniqueAssignments = $policyPolicySetBaseQueryUniqueAssignments.where( { $_.PolicyDefinitionId -eq $tenantPolicySet.Id }).PolicyAssignmentId
- $policySetUniqueAssignmentsArray = [System.Collections.ArrayList]@()
- foreach ($policySetUniqueAssignment in $policySetUniqueAssignments) {
- $null = $policySetUniqueAssignmentsArray.Add($policySetUniqueAssignment)
- }
- $policySetUniqueAssignmentsCount = ($policySetUniqueAssignments).count
- if ($policySetUniqueAssignmentsCount -gt 0) {
- $policySetUniqueAssignmentsList = "($($policySetUniqueAssignmentsArray -join "$CsvDelimiterOpposite "))"
- $policySetUniqueAssignment = "$policySetUniqueAssignmentsCount $policySetUniqueAssignmentsList"
- }
- else {
- $policySetUniqueAssignment = $policySetUniqueAssignmentsCount
- }
-
- $policySetPoliciesArray = [System.Collections.ArrayList]@()
- $policySetPoliciesArrayClean = [System.Collections.ArrayList]@()
- $policySetPoliciesArrayIdOnly = [System.Collections.ArrayList]@()
- $policySetPoliciesBuiltinArrayIdOnlyCSV = [System.Collections.ArrayList]@()
- $policySetPoliciesStaticArrayIdOnlyCSV = [System.Collections.ArrayList]@()
- $policySetPoliciesCustomArrayIdOnlyCSV = [System.Collections.ArrayList]@()
- foreach ($policyPolicySet in $tenantPolicySet.PolicySetPolicyIds) {
- $hlpPolicyDef = ($htCacheDefinitionsPolicy).($policyPolicySet)
-
- if ($hlpPolicyDef.Type -eq 'Builtin' -or $hlpPolicyDef.Type -eq 'Static') {
- $null = $policySetPoliciesArray.Add("$($hlpPolicyDef.LinkToAzAdvertizer) ($policyPolicySet)")
- if ($hlpPolicyDef.Type -eq 'Builtin') {
- $null = $policySetPoliciesBuiltinArrayIdOnlyCSV.Add($policyPolicySet -replace '/providers/microsoft.authorization/policydefinitions/')
- }
- if ($hlpPolicyDef.Type -eq 'Static') {
- $null = $policySetPoliciesStaticArrayIdOnlyCSV.Add($policyPolicySet -replace '/providers/microsoft.authorization/policydefinitions/')
- }
- }
- else {
- $null = $policySetPoliciesCustomArrayIdOnlyCSV.Add($policyPolicySet)
- if ($hlpPolicyDef.DisplayName) {
- if ([string]::IsNullOrEmpty($hlpPolicyDef.DisplayName)) {
- $displayName = 'noDisplayNameGiven'
- }
- else {
- $displayName = $hlpPolicyDef.DisplayName
- }
- }
- else {
- $displayName = 'noDisplayNameGiven'
- }
- $null = $policySetPoliciesArray.Add("
$($displayName -replace '<', '<' -replace '>', '>') ($policyPolicySet)")
- }
-
- if ($hlpPolicyDef.DisplayName) {
- if ([string]::IsNullOrEmpty($hlpPolicyDef.DisplayName)) {
- $displayName = 'noDisplayNameGiven'
- }
- else {
- $displayName = $hlpPolicyDef.DisplayName
- }
- }
- else {
- $displayName = 'noDisplayNameGiven'
- }
-
- $null = $policySetPoliciesArrayClean.Add("$($displayName) ($policyPolicySet)")
- $null = $policySetPoliciesArrayIdOnly.Add($policyPolicySet)
- }
-
- if ($policySetPoliciesArrayIdOnly.Count -eq 0) {
- $policySetPoliciesArrayIdOnly = $null
- }
-
- if ($policySetPoliciesBuiltinArrayIdOnlyCSV.Count -eq 0) {
- $policySetPoliciesBuiltinArrayIdOnlyCSV = $null
- }
- if ($policySetPoliciesStaticArrayIdOnlyCSV.Count -eq 0) {
- $policySetPoliciesStaticArrayIdOnlyCSV = $null
- }
- if ($policySetPoliciesCustomArrayIdOnlyCSV.Count -eq 0) {
- $policySetPoliciesCustomArrayIdOnlyCSV = $null
- }
-
- $policySetPoliciesCount = ($policySetPoliciesArray).count
- if ($policySetPoliciesCount -gt 0) {
- $policiesUsed = "$policySetPoliciesCount ($(($policySetPoliciesArray | Sort-Object) -join "$CsvDelimiterOpposite "))"
- $policiesUsedClean = "$policySetPoliciesCount ($(($policySetPoliciesArrayClean | Sort-Object) -join "$CsvDelimiterOpposite "))"
- }
- else {
- $policiesUsed = '0 really?'
- $policiesUsedClean = '0 really?'
- }
-
- if ($tenantPolicySet.Json.properties.metadata.version) {
- $policySetVersion = $tenantPolicySet.Json.properties.metadata.version
- }
- else {
- $policySetVersion = 'n/a'
- }
-
- if ($tenantPolicySet.Type -eq 'Custom') {
- #inscopeOrNot
- if ($getMgParentName -ne 'Tenant Root') {
- if ($mgsAndSubs.MgId -contains ($tenantPolicySet.ScopeId)) {
- $null = $custompolicySetsInScopeArray.Add($tenantPolicySet)
- }
- if ($mgsAndSubs.SubscriptionId -contains ($tenantPolicySet.ScopeId)) {
- $null = $custompolicySetsInScopeArray.Add($tenantPolicySet)
- }
- }
-
- $createdOn = ''
- $createdBy = ''
- $createdByJson = ''
- $updatedOn = ''
- $updatedBy = ''
- $updatedByJson = ''
- if ($tenantPolicySet.Json.properties.metadata.createdOn) {
- $createdOn = $tenantPolicySet.Json.properties.metadata.createdOn
- }
- if ($tenantPolicySet.Json.properties.metadata.createdBy) {
- $createdBy = $tenantPolicySet.Json.properties.metadata.createdBy
- $createdByJson = $createdBy
- if ($htIdentitiesWithRoleAssignmentsUnique.($createdBy)) {
- $createdByJson = $htIdentitiesWithRoleAssignmentsUnique.($createdBy).detailsJson
- $createdBy = $htIdentitiesWithRoleAssignmentsUnique.($createdBy).details
- }
- }
- if ($tenantPolicySet.Json.properties.metadata.updatedOn) {
- $updatedOn = $tenantPolicySet.Json.properties.metadata.updatedOn
- }
- if ($tenantPolicySet.Json.properties.metadata.updatedBy) {
- $updatedBy = $tenantPolicySet.Json.properties.metadata.updatedBy
- $updatedByJson = $updatedBy
- if ($htIdentitiesWithRoleAssignmentsUnique.($updatedBy)) {
- $updatedByJson = $htIdentitiesWithRoleAssignmentsUnique.($updatedBy).detailsJson
- $updatedBy = $htIdentitiesWithRoleAssignmentsUnique.($updatedBy).details
-
- }
- }
-
- $null = $script:customPolicySetsDetailed.Add([PSCustomObject]@{
- Type = 'Custom'
- ScopeMGLevel = $tenantPolicySet.ScopeMGLevel
- Scope = $tenantPolicySet.ScopeMgSub
- ScopeId = $tenantPolicySet.ScopeId
- PolicySetDisplayName = $tenantPolicySet.DisplayName
- PolicySetDefinitionName = $tenantPolicySet.Name
- PolicySetDefinitionId = $tenantPolicySet.PolicyDefinitionId
- PolicySetCategory = $tenantPolicySet.Category
- UniqueAssignments = $policySetUniqueAssignment
- PoliciesUsed = $policiesUsed
- PoliciesUsedClean = $policiesUsedClean
- CreatedOn = $createdOn
- CreatedBy = $createdBy
- UpdatedOn = $updatedOn
- UpdatedBy = $updatedBy
- #Json = [string]($tenantPolicySet.Json | ConvertTo-Json -Depth 99 -EnumsAsStrings)
- ALZ = $tenantPolicySet.ALZ
- ALZState = $tenantPolicySet.ALZState
- ALZLatestVer = $tenantPolicySet.ALZLatestVer
- ALZIdentificationLevel = $tenantPolicySet.ALZIdentificationLevel
- ALZPolicySetName = $tenantPolicySet.ALZPolicySetName
- })
-
- $null = $script:tenantPolicySetsDetailed.Add([PSCustomObject]@{
- Type = 'Custom'
- ScopeMGLevel = $tenantPolicySet.ScopeMGLevel
- Scope = $tenantPolicySet.ScopeMgSub
- ScopeId = $tenantPolicySet.ScopeId
- PolicySetDisplayName = $tenantPolicySet.DisplayName
- PolicySetDescription = $tenantPolicySet.Description
- PolicySetDefinitionName = $tenantPolicySet.Name
- PolicySetDefinitionId = $tenantPolicySet.PolicyDefinitionId
- PolicySetCategory = $tenantPolicySet.Category
- PolicySetVersion = $tenantPolicySet.Version
- UniqueAssignmentsCount = $policySetUniqueAssignmentsCount
- UniqueAssignments = $policySetUniqueAssignments
- PoliciesUsedCount = $policySetPoliciesCount
- PoliciesUsedBuiltinCount = $policySetPoliciesBuiltinArrayIdOnlyCSV.Count
- PoliciesUsedStaticCount = $policySetPoliciesStaticArrayIdOnlyCSV.Count
- PoliciesUsedCustomCount = $policySetPoliciesCustomArrayIdOnlyCSV.Count
- PoliciesUsed = $policySetPoliciesArrayClean
- PoliciesUsed4JSON = $policySetPoliciesArrayIdOnly
- PoliciesUsedBuiltin = $policySetPoliciesBuiltinArrayIdOnlyCSV -join "$CsvDelimiterOpposite "
- PoliciesUsedStatic = $policySetPoliciesStaticArrayIdOnlyCSV -join "$CsvDelimiterOpposite "
- PoliciesUsedCustom = $policySetPoliciesCustomArrayIdOnlyCSV -join "$CsvDelimiterOpposite "
- CreatedOn = $createdOn
- CreatedBy = $createdBy
- CreatedByJson = $createdByJson
- UpdatedOn = $updatedOn
- UpdatedBy = $updatedBy
- UpdatedByJson = $updatedByJson
- #Json = [string]($tenantPolicySet.Json | ConvertTo-Json -Depth 99 -EnumsAsStrings)
- Json = $tenantPolicySet.Json
- ALZ = $tenantPolicySet.ALZ
- ALZState = $tenantPolicySet.ALZState
- ALZLatestVer = $tenantPolicySet.ALZLatestVer
- ALZIdentificationLevel = $tenantPolicySet.ALZIdentificationLevel
- ALZPolicySetName = $tenantPolicySet.ALZPolicySetName
- })
-
- }
- else {
- $null = $script:tenantPolicySetsDetailed.Add([PSCustomObject]@{
- Type = 'BuiltIn'
- ScopeMGLevel = $null
- Scope = $null
- ScopeId = $null
- PolicySetDisplayName = $tenantPolicySet.DisplayName
- PolicySetDescription = $tenantPolicySet.Description
- PolicySetDefinitionName = $tenantPolicySet.Name
- PolicySetDefinitionId = $tenantPolicySet.PolicyDefinitionId
- PolicySetCategory = $tenantPolicySet.Category
- PolicySetVersion = $tenantPolicySet.Version
- UniqueAssignmentsCount = $policySetUniqueAssignmentsCount
- UniqueAssignments = $policySetUniqueAssignments
- PoliciesUsedCount = $policySetPoliciesCount
- PoliciesUsedBuiltinCount = $policySetPoliciesBuiltinArrayIdOnlyCSV.Count
- PoliciesUsedStaticCount = $policySetPoliciesStaticArrayIdOnlyCSV.Count
- PoliciesUsedCustomCount = $policySetPoliciesCustomArrayIdOnlyCSV.Count
- PoliciesUsed = $policySetPoliciesArrayClean
- PoliciesUsed4JSON = $policySetPoliciesArrayIdOnly
- PoliciesUsedBuiltin = $policySetPoliciesBuiltinArrayIdOnlyCSV -join "$CsvDelimiterOpposite "
- PoliciesUsedStatic = $policySetPoliciesStaticArrayIdOnlyCSV -join "$CsvDelimiterOpposite "
- PoliciesUsedCustom = $policySetPoliciesCustomArrayIdOnlyCSV -join "$CsvDelimiterOpposite "
- CreatedOn = ''
- CreatedBy = ''
- CreatedByJson = $null
- UpdatedOn = ''
- UpdatedBy = ''
- UpdatedByJson = $null
- #Json = [string]($tenantPolicySet.Json | ConvertTo-Json -Depth 99 -EnumsAsStrings)
- Json = $tenantPolicySet.Json
- ALZ = $tenantPolicySet.ALZ
- ALZState = $tenantPolicySet.ALZState
- ALZLatestVer = $tenantPolicySet.ALZLatestVer
- ALZIdentificationLevel = $tenantPolicySet.ALZIdentificationLevel
- ALZPolicySetName = $tenantPolicySet.ALZPolicySetName
- })
- }
- }
-
- if (-not $NoCsvExport) {
- $csvFilename = "$($filename)_PolicySetDefinitions"
- Write-Host " Exporting PolicySetDefinitions CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
- $tenantPolicySetsDetailed | Select-Object -ExcludeProperty UniqueAssignments, PoliciesUsed, PoliciesUsed4JSON, CreatedByJson, UpdatedByJson, Json | Sort-Object -Property Type, Scope, PolicySetDefinitionId | Export-Csv -Encoding utf8 -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter $csvDelimiter -NoTypeInformation
- }
-
- if ($getMgParentName -eq 'Tenant Root') {
- if ($tenantCustompolicySetsCount -gt $LimitPOLICYPolicySetDefinitionsScopedTenant * ($LimitCriticalPercentage / 100)) {
- $faimage = "
"
- }
- else {
- $faimage = "
"
- }
-
- if ($tenantCustompolicySetsCount -gt 0) {
- $tfCount = $tenantCustompolicySetsCount
- $htmlTableId = 'TenantSummary_customPolicySets'
- [void]$htmlTenantSummary.AppendLine(@"
-
$faimage $tenantCustompolicySetsCount Custom PolicySet definitions ($scopeNamingSummary) (Limit: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant)
-
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-ScopeId
-PolicySet DisplayName
-PolicySet Name
-PolicySetId
-Category
-ALZ
-Unique assignments
-Policies used in PolicySet
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlSUMMARYtenanttotalcustompolicySets = $null
- $htmlSUMMARYtenanttotalcustompolicySets = foreach ($customPolicySet in $customPolicySetsDetailed | Sort-Object @{Expression = { $_.Scope } }, @{Expression = { $_.PolicySetDisplayName } }, @{Expression = { $_.PolicySetDefinitionId } }) {
- @"
-
-$($customPolicySet.Scope)
-$($customPolicySet.ScopeId)
-$($customPolicySet.PolicySetDisplayName -replace '<', '<' -replace '>', '>')
-$($customPolicySet.PolicySetDefinitionName -replace '<', '<' -replace '>', '>')
-$($customPolicySet.PolicySetDefinitionId -replace '<', '<' -replace '>', '>')
-$($customPolicySet.PolicySetCategory -replace '<', '<' -replace '>', '>')
-$($customPolicySet.ALZ)
-$($customPolicySet.UniqueAssignments -replace '<', '<' -replace '>', '>')
-$($customPolicySet.PoliciesUsed)
-$($customPolicySet.CreatedOn)
-$($customPolicySet.CreatedBy)
-$($customPolicySet.UpdatedOn)
-$($customPolicySet.UpdatedBy)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYtenanttotalcustompolicySets)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$tenantCustomPolicySetsCount Custom PolicySet definitions ($scopeNamingSummary)
-"@)
- }
- }
- #SUMMARY NOT tenant total custom policySet definitions
- else {
- $faimage = "
"
- if ($tenantCustompolicySetsCount -gt $LimitPOLICYPolicySetDefinitionsScopedTenant * ($LimitCriticalPercentage / 100)) {
- $faimage = "
"
- }
- else {
- $faimage = "
"
- }
-
- if ($tenantCustompolicySetsCount -gt 0) {
- $custompolicySetsFromSuperiorMGs = $tenantCustompolicySetsCount - (($custompolicySetsInScopeArray).count)
- }
- else {
- $custompolicySetsFromSuperiorMGs = '0'
- }
-
- if ($tenantCustompolicySetsCount -gt 0) {
- $tfCount = $tenantCustompolicySetsCount
- $htmlTableId = 'TenantSummary_customPolicySets'
- [void]$htmlTenantSummary.AppendLine(@"
-
$faimage $tenantCustomPolicySetsCount Custom PolicySet definitions $scopeNamingSummary ($custompolicySetsFromSuperiorMGs from superior scopes) (Limit: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant)
-
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-Scope Id
-PolicySet DisplayName
-PolicySet Name
-PolicySetId
-Category
-ALZ
-Unique assignments
-Policies used in PolicySet
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlSUMMARYtenanttotalcustompolicySets = $null
- $htmlSUMMARYtenanttotalcustompolicySets = foreach ($customPolicySet in $customPolicySetsDetailed | Sort-Object @{Expression = { $_.Scope } }, @{Expression = { $_.PolicySetDisplayName } }, @{Expression = { $_.PolicySetDefinitionId } }) {
- @"
-
-$($customPolicySet.Scope)
-$($customPolicySet.ScopeId)
-$($customPolicySet.PolicySetDisplayName -replace '<', '<' -replace '>', '>')
-$($customPolicySet.PolicySetDefinitionName -replace '<', '<' -replace '>', '>')
-$($customPolicySet.PolicySetDefinitionId -replace '<', '<' -replace '>', '>')
-$($customPolicySet.PolicySetCategory -replace '<', '<' -replace '>', '>')
-$($customPolicySet.ALZ)
-$($customPolicySet.UniqueAssignments -replace '<', '<' -replace '>', '>')
-$($customPolicySet.PoliciesUsed)
-$($customPolicySet.CreatedOn)
-$($customPolicySet.CreatedBy)
-$($customPolicySet.UpdatedOn)
-$($customPolicySet.UpdatedBy)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYtenanttotalcustompolicySets)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$tenantCustomPolicySetsCount Custom PolicySet definitions ($scopeNamingSummary)
-"@)
- }
- }
- $endCustPolSetLoop = Get-Date
- Write-Host " Custom PolicySet processing duration: $((New-TimeSpan -Start $startCustPolSetLoop -End $endCustPolSetLoop).TotalMinutes) minutes ($((New-TimeSpan -Start $startCustPolSetLoop -End $endCustPolSetLoop).TotalSeconds) seconds)"
- #endregion SUMMARYtenanttotalcustompolicySets
-
- #region SUMMARYCustompolicySetOrphandedTenantRoot
- Write-Host ' processing TenantSummary Custom PolicySet definitions orphaned'
- if ($getMgParentName -eq 'Tenant Root') {
- $custompolicySetSetsOrphaned = [System.Collections.ArrayList]@()
- foreach ($custompolicySetAll in $tenantCustomPolicySets) {
- if (($policyPolicySetBaseQueryUniqueCustomDefinitions).count -eq 0) {
- $null = $custompolicySetSetsOrphaned.Add($custompolicySetAll)
- }
- else {
- if ($policyPolicySetBaseQueryUniqueCustomDefinitions -notcontains ($custompolicySetAll.Id)) {
- $null = $custompolicySetSetsOrphaned.Add($custompolicySetAll)
- }
- }
- }
-
- $arraycustompolicySetSetsOrphanedFinalIncludingResourceGroups = [System.Collections.ArrayList]@()
- foreach ($customPolicySetOrphaned in $custompolicySetSetsOrphaned) {
- if (($htCacheAssignmentsPolicyOnResourceGroupsAndResources).values.properties.PolicyDefinitionId -notcontains $customPolicySetOrphaned.PolicyDefinitionId) {
- $null = $arraycustompolicySetSetsOrphanedFinalIncludingResourceGroups.Add($customPolicySetOrphaned)
- }
- }
-
- if (($arraycustompolicySetSetsOrphanedFinalIncludingResourceGroups).count -gt 0) {
- $tfCount = ($arraycustompolicySetSetsOrphanedFinalIncludingResourceGroups).count
- $htmlTableId = 'TenantSummary_customPolicySetsOrphaned'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arraycustompolicySetSetsOrphanedFinalIncludingResourceGroups).count) Orphaned Custom PolicySet definitions ($scopeNamingSummary)
-
-
Download CSV
semicolon |
comma
-
-
-
-PolicySet DisplayName
-PolicySetId
-
-
-
-"@)
- $htmlSUMMARYCustompolicySetOrphandedTenantRoot = $null
- $htmlSUMMARYCustompolicySetOrphandedTenantRoot = foreach ($custompolicySetOrphaned in $arraycustompolicySetSetsOrphanedFinalIncludingResourceGroups | Sort-Object @{Expression = { $_.PolicyDefinitionId } }, @{Expression = { $_.DisplayName } }) {
- @"
-
-$($custompolicySetOrphaned.DisplayName)
-$($custompolicySetOrphaned.PolicyDefinitionId)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYCustompolicySetOrphandedTenantRoot)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arraycustompolicySetSetsOrphanedFinalIncludingResourceGroups).count) Orphaned Custom PolicySet definitions ($scopeNamingSummary)
-"@)
- }
- }
- #SUMMARY Custom policySetSets Orphanded NOT TenantRoot
- else {
- $arraycustompolicySetsOrphanedFinalIncludingResourceGroups = [System.Collections.ArrayList]@()
- foreach ($custompolicySetAll in $tenantCustomPolicySets) {
- $isOrphaned = 'unknown'
- if (($policyPolicySetBaseQueryUniqueCustomDefinitions).count -eq 0) {
- $isOrphaned = 'potentially'
- }
- else {
- if ($policyPolicySetBaseQueryUniqueCustomDefinitions -notcontains $custompolicySetAll.Id) {
- $isOrphaned = 'potentially'
- }
- }
-
- if ($isOrphaned -eq 'potentially') {
- $isInScope = 'unknown'
- if ($custompolicySetAll.PolicyDefinitionId -like '/providers/Microsoft.Management/managementGroups/*') {
- $policySetScopedMgSub = $custompolicySetAll.PolicyDefinitionId -replace '/providers/Microsoft.Management/managementGroups/', '' -replace '/.*'
- if ($mgsAndSubs.MgId -contains ($policySetScopedMgSub)) {
- $isInScope = 'inScope'
- }
- }
- elseif ($custompolicySetAll.PolicyDefinitionId -like '/subscriptions/*') {
- $policySetScopedMgSub = $custompolicySetAll.PolicyDefinitionId -replace '/subscriptions/', '' -replace '/.*'
- if ($mgsAndSubs.SubscriptionId -contains ($policySetScopedMgSub)) {
- $isInScope = 'inScope'
- }
- }
- else {
- Write-Host 'unexpected'
- }
-
- if ($isInScope -eq 'inScope') {
- if (($htCacheAssignmentsPolicyOnResourceGroupsAndResources).values.properties.PolicyDefinitionId -notcontains $custompolicySetAll.PolicyDefinitionId) {
- $null = $arraycustompolicySetsOrphanedFinalIncludingResourceGroups.Add($custompolicySetAll)
- }
- }
- }
- }
-
- if (($arraycustompolicySetsOrphanedFinalIncludingResourceGroups).count -gt 0) {
- $tfCount = ($arraycustompolicySetsOrphanedFinalIncludingResourceGroups).count
- $htmlTableId = 'TenantSummary_customPolicySetsOrphaned'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arraycustompolicySetsOrphanedFinalIncludingResourceGroups).count) Orphaned Custom PolicySet definitions ($scopeNamingSummary)
-
-
Download CSV
semicolon |
comma
-
-
-
-PolicySet DisplayName
-PolicySetId
-
-
-
-"@)
- $htmlSUMMARYCustompolicySetOrphandedTenantRoot = $null
- $htmlSUMMARYCustompolicySetOrphandedTenantRoot = foreach ($custompolicySetOrphaned in $arraycustompolicySetsOrphanedFinalIncludingResourceGroups | Sort-Object @{Expression = { $_.PolicyDefinitionId } }, @{Expression = { $_.DisplayName } }) {
- @"
-
-$($custompolicySetOrphaned.DisplayName)
-$($custompolicySetOrphaned.policyDefinitionId)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYCustompolicySetOrphandedTenantRoot)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arraycustompolicySetsOrphanedFinalIncludingResourceGroups).count) Orphaned Custom PolicySet definitions ($scopeNamingSummary)
-"@)
- }
- }
- #endregion SUMMARYCustompolicySetOrphandedTenantRoot
-
- #region SUMMARYPolicyParityCustomBuiltIn
- Write-Host ' processing TenantSummary Policy parity custom built-in'
-
- if ($arrayCustomBuiltInPolicyParity.Count -gt 0) {
- $tfCount = $arrayCustomBuiltInPolicyParity.Count
- $htmlTableId = 'TenantSummary_PolicyCustomBuiltInParity'
- [void]$htmlTenantSummary.AppendLine(@"
-
$($arrayCustomBuiltInPolicyParity.Count) custom Policy definition(s) built-in Policy rule parity
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Policy Name
-Policy DisplayName
-Policy Category
-Policy Id
-# match built-in
-Built-In Policy
-
-
-
-"@)
-
- $htmlSUMMARYPolicyCustomBuiltInParity = $null
- $htmlSUMMARYPolicyCustomBuiltInParity = foreach ($entry in $arrayCustomBuiltInPolicyParity | Sort-Object -Property CustomPolicyId) {
- $arrayBuiltinsRef = @()
- foreach ($builtInPolicyId in $entry.BuiltInPolicyId) {
- $arrayBuiltinsRef += "$($htCacheDefinitionsPolicy.($builtInPolicyId).DisplayName) ($($builtInPolicyId -replace '.*/'))"
- }
- $builtInPolicyAzA = $arrayBuiltinsRef -join ', '
- @"
-
-$($entry.CustomPolicyName)
-$($entry.CustomPolicyDisplayName)
-$($entry.CustomPolicyCategory)
-$($entry.CustomPolicyId)
-$($entry.MatchBuiltinPolicyCount)
-$($builtInPolicyAzA)
-
-"@
- }
-
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYPolicyCustomBuiltInParity)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No custom Policy definition(s) built-in Policy rule parity
-'@)
- }
- #endregion SUMMARYPolicyParityCustomBuiltIn
-
- #region SUMMARYALZPolicies
- Write-Host ' processing TenantSummary ALZPolicies'
-
- if (-not $NoALZPolicyVersionChecker) {
-
- $alzPoliciesInTenant = [System.Collections.ArrayList]@()
- #policies
- foreach ($policy in ($htCacheDefinitionsPolicy).Values.where({ $_.ALZ -eq $true })) {
- if ($policy.ALZState -ne 'obsolete' -and $policy.ALZState -ne 'unknown') {
- $ALZVersion = $alzPolicies.($policy.ALZPolicyName).latestVersion
- $azAdvertizerUrl = "https://www.azadvertizer.net/azpolicyadvertizer/$($policy.ALZPolicyName).html"
- }
- else {
- $ALZVersion = ''
- $azAdvertizerUrl = ''
- }
- $null = $alzPoliciesInTenant.Add([PSCustomObject]@{
- Type = 'Policy'
- PolicyName = $policy.Name
- PolicyId = $policy.PolicyDefinitionId
- PolicyVersion = $policy.Version
- PolicyScope = $policy.ScopeMgSub
- PolicyScopeId = $policy.ScopeId
- ALZPolicyName = $policy.ALZPolicyName
- ALZVersion = $ALZVersion
- ALZState = $policy.ALZState
- InTenant = $true
- DetectedBy = $policy.ALZIdentificationLevel
- AzAdvertizerUrl = $azAdvertizerUrl
- })
- }
- foreach ($alzPolicy in $alzPolicies.keys) {
- if ($alzPolicies.($alzPolicy).status -eq 'Prod') {
- if ($alzPoliciesInTenant.PolicyName -notcontains $alzPolicy) {
- $null = $alzPoliciesInTenant.Add([PSCustomObject]@{
- Type = 'Policy'
- PolicyName = 'n/a'
- PolicyId = 'n/a'
- PolicyVersion = 'n/a'
- PolicyScope = 'n/a'
- PolicyScopeId = 'n/a'
- ALZPolicyName = $alzPolicy
- ALZVersion = $alzPolicies.($alzPolicy).latestVersion
- ALZState = ''
- InTenant = $false
- DetectedBy = 'ALZ GitHub repository'
- AzAdvertizerUrl = "https://www.azadvertizer.net/azpolicyadvertizer/$($alzPolicy).html"
- })
- }
- }
- }
-
- #policysets
- foreach ($policySet in ($htCacheDefinitionsPolicySet).Values.where({ $_.ALZ -eq $true })) {
-
- if ($policySet.ALZState -ne 'obsolete' -and $policySet.ALZState -ne 'unknown') {
- $ALZVersion = $alzPolicySets.($policySet.ALZPolicySetName).latestVersion
- $azAdvertizerUrl = "https://www.azadvertizer.net/azpolicyinitiativesadvertizer/$($policySet.ALZPolicySetName).html"
- }
- else {
- $ALZVersion = ''
- $azAdvertizerUrl = ''
- }
- $null = $alzPoliciesInTenant.Add([PSCustomObject]@{
- Type = 'PolicySet'
- PolicyName = $policySet.Name
- PolicyId = $policySet.PolicyDefinitionId
- PolicyVersion = $policySet.Version
- PolicyScope = $policySet.ScopeMgSub
- PolicyScopeId = $policySet.ScopeId
- ALZPolicyName = $policySet.ALZPolicySetName
- ALZVersion = $ALZVersion
- ALZState = $policySet.ALZState
- InTenant = $true
- DetectedBy = $policySet.ALZIdentificationLevel
- AzAdvertizerUrl = $azAdvertizerUrl
- })
- }
-
- foreach ($alzPolicySet in $alzPolicySets.keys) {
- if ($alzPolicySets.($alzPolicySet).status -eq 'Prod') {
- if ($alzPoliciesInTenant.PolicyName -notcontains $alzPolicySet) {
- $null = $alzPoliciesInTenant.Add([PSCustomObject]@{
- Type = 'PolicySet'
- PolicyName = 'n/a'
- PolicyId = 'n/a'
- PolicyVersion = 'n/a'
- PolicyScope = 'n/a'
- PolicyScopeId = 'n/a'
- ALZPolicyName = $alzPolicySet
- ALZVersion = $alzPolicySets.($alzPolicySet).latestVersion
- ALZState = ''
- InTenant = $false
- DetectedBy = 'ALZ GitHub repository'
- AzAdvertizerUrl = "https://www.azadvertizer.net/azpolicyinitiativesadvertizer/$($alzPolicySet).html"
- })
- }
- }
- }
-
- if ($alzPoliciesInTenant.Count -gt 0) {
- $tfCount = $alzPoliciesInTenant.Count
- $htmlTableId = 'TenantSummary_ALZPolicies'
- $abbrALZ = "
"
- [void]$htmlTenantSummary.AppendLine(@"
-
Azure Landing Zones (ALZ) Policy Version Checker
-
-
-
Azure Landing Zones (ALZ) GitHub
-
Download CSV
semicolon |
comma
-
-
-
-Type
-Policy Name (Id)
-Policy Version
-Policy Scope
-Policy Scope Id
-ALZ Policy Name (Id)
-ALZ Policy Version
-ALZ State$($abbrALZ)
-Exists in tenant
-Detection method
-AzAdvertizer Link
-
-
-
-"@)
-
- $htmlSUMMARYALZPolicyVersionChecker = $null
- $exemptionData4CSVExport = [System.Collections.ArrayList]@()
- $alzPoliciesInTenantSorted = $alzPoliciesInTenant | Sort-Object -Property PolicyName, PolicyId, ALZPolicyName, Type
- $htmlSUMMARYALZPolicyVersionChecker = foreach ($entry in $alzPoliciesInTenantSorted) {
- if ([string]::IsNullOrWhiteSpace($entry.AzAdvertizerUrl)) {
- $link = ''
- }
- else {
- $link = "AzA Link "
- }
- @"
-
-$($entry.Type)
-$($entry.PolicyName)
-$($entry.PolicyVersion)
-$($entry.PolicyScope)
-$($entry.PolicyScopeId)
-$($entry.ALZPolicyName)
-$($entry.ALZVersion)
-$($entry.ALZState)
-$($entry.InTenant)
-$($entry.DetectedBy)
-$link
-
-"@
- }
-
- if (-not $NoCsvExport) {
- Write-Host "Exporting 'Azure Landing Zones (ALZ) Policy Version Checker' CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName)_ALZPolicyVersionChecker.csv'"
- $alzPoliciesInTenantSorted | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_ALZPolicyVersionChecker.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
-
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYALZPolicyVersionChecker)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
Azure Landing Zones (ALZ) Policy Version Checker
-'@)
- }
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Azure Landing Zones (ALZ) Policy Version Checker (parameter -NoALZPolicyVersionChecker = $NoALZPolicyVersionChecker)
-"@)
- }
- #endregion SUMMARYALZPolicies
-
- $startcustpolsetdeprpol = Get-Date
- #region SUMMARYPolicySetsDeprecatedPolicy
- Write-Host ' processing TenantSummary Custom PolicySet definitions using deprected Policy'
- $policySetsDeprecated = [System.Collections.ArrayList]@()
- $customPolicySetsCount = ($tenantCustomPolicySets).count
- if ($customPolicySetsCount -gt 0) {
- foreach ($polSetDef in $tenantCustomPolicySets) {
- foreach ($polsetPolDefId in $polSetDef.PolicySetPolicyIds) {
- $hlpDeprecatedPolicySet = (($htCacheDefinitionsPolicy).($polsetPolDefId))
- if ($hlpDeprecatedPolicySet.Type -eq 'BuiltIn') {
- if ($hlpDeprecatedPolicySet.Deprecated -eq $true -or ($hlpDeprecatedPolicySet.DisplayName).StartsWith('[Deprecated]', 'CurrentCultureIgnoreCase')) {
- $null = $policySetsDeprecated.Add([PSCustomObject]@{
- PolicySetDisplayName = $polSetDef.DisplayName
- PolicySetDefinitionId = $polSetDef.PolicyDefinitionId
- PolicyDisplayName = $hlpDeprecatedPolicySet.DisplayName
- PolicyId = $hlpDeprecatedPolicySet.Id
- DeprecatedProperty = $hlpDeprecatedPolicySet.Deprecated
- })
- }
- }
- }
- }
- }
-
- if (($policySetsDeprecated).count -gt 0) {
- $tfCount = ($policySetsDeprecated).count
- $htmlTableId = 'TenantSummary_policySetsDeprecated'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($policySetsDeprecated).count) Custom PolicySet definitions / deprecated built-in Policy
-
-
-
Download CSV
semicolon |
comma
-
-
-
-PolicySet DisplayName
-PolicySetId
-Policy DisplayName
-PolicyId
-Deprecated Property
-
-
-
-"@)
- $htmlSUMMARYPolicySetsDeprecatedPolicy = $null
- $htmlSUMMARYPolicySetsDeprecatedPolicy = foreach ($policySetDeprecated in $policySetsDeprecated | Sort-Object @{Expression = { $_.PolicySetDisplayName } }, @{Expression = { $_.PolicySetDefinitionId } }) {
-
- if ($policySetDeprecated.DeprecatedProperty -eq $true) {
- $deprecatedProperty = 'true'
- }
- else {
- $deprecatedProperty = 'false'
- }
- @"
-
-$($policySetDeprecated.PolicySetDisplayName -replace '<', '<' -replace '>', '>')
-$($policySetDeprecated.PolicySetDefinitionId -replace '<', '<' -replace '>', '>')
-$($policySetDeprecated.PolicyDisplayName -replace '<', '<' -replace '>', '>')
-$($policySetDeprecated.PolicyId -replace '<', '<' -replace '>', '>')
-$deprecatedProperty
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYPolicySetsDeprecatedPolicy)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($policySetsDeprecated).count) PolicySets / deprecated built-in Policy
-"@)
- }
- #endregion SUMMARYPolicySetsDeprecatedPolicy
- $endcustpolsetdeprpol = Get-Date
- Write-Host " processing PolicySetsDeprecatedPolicy duration: $((New-TimeSpan -Start $startcustpolsetdeprpol -End $endcustpolsetdeprpol).TotalSeconds) seconds"
-
- $startcustpolassdeprpol = Get-Date
- #region SUMMARYPolicyAssignmentsDeprecatedPolicy
- Write-Host ' processing TenantSummary PolicyAssignments using deprecated Policy'
- $policyAssignmentsDeprecated = [System.Collections.ArrayList]@()
- foreach ($policyAssignmentAll in ($htCacheAssignmentsPolicy).Values) {
-
- $hlpAssignmentDeprecatedPolicy = $policyAssignmentAll.Assignment
- $hlpPolicyDefinitionId = ($hlpAssignmentDeprecatedPolicy.properties.policyDefinitionId).ToLower()
- #policySet
- if ($($htCacheDefinitionsPolicySet).(($hlpPolicyDefinitionId))) {
- foreach ($polsetPolDefId in $($htCacheDefinitionsPolicySet).(($hlpPolicyDefinitionId)).PolicySetPolicyIds) {
- $hlpDeprecatedAssignment = (($htCacheDefinitionsPolicy).(($polsetPolDefId)))
- if ($hlpDeprecatedAssignment.type -eq 'BuiltIn') {
- if ($hlpDeprecatedAssignment.Deprecated -eq $true) {
- $null = $policyAssignmentsDeprecated.Add([PSCustomObject]@{
- PolicyAssignmentDisplayName = $hlpAssignmentDeprecatedPolicy.properties.displayName
- PolicyAssignmentId = ($hlpAssignmentDeprecatedPolicy.id).Tolower()
- PolicyDisplayName = $hlpDeprecatedAssignment.DisplayName
- PolicyId = $hlpDeprecatedAssignment.Id
- PolicySetDisplayName = ($htCacheDefinitionsPolicySet).(($hlpPolicyDefinitionId)).DisplayName
- PolicySetId = ($htCacheDefinitionsPolicySet).(($hlpPolicyDefinitionId)).PolicyDefinitionId
- PolicyType = 'PolicySet'
- DeprecatedProperty = $hlpDeprecatedAssignment.Deprecated
- })
- }
- }
- }
- }
-
- #Policy
- $hlpDeprecatedAssignmentPol = ($htCacheDefinitionsPolicy).(($hlpPolicyDefinitionId))
- if ($hlpDeprecatedAssignmentPol) {
- if ($hlpDeprecatedAssignmentPol.type -eq 'BuiltIn') {
- if ($hlpDeprecatedAssignmentPol.Deprecated -eq $true) {
- $null = $policyAssignmentsDeprecated.Add([PSCustomObject]@{
- PolicyAssignmentDisplayName = $hlpAssignmentDeprecatedPolicy.properties.displayName
- PolicyAssignmentId = ($hlpAssignmentDeprecatedPolicy.id).Tolower()
- PolicyDisplayName = $hlpDeprecatedAssignmentPol.DisplayName
- PolicyId = $hlpDeprecatedAssignmentPol.Id
- PolicyType = 'Policy'
- DeprecatedProperty = $hlpDeprecatedAssignmentPol.Deprecated
- PolicySetDisplayName = 'n/a'
- PolicySetId = 'n/a'
- })
- }
- }
- }
- }
-
-
- if (($policyAssignmentsDeprecated).count -gt 0) {
- $tfCount = ($policyAssignmentsDeprecated).count
- $htmlTableId = 'TenantSummary_policyAssignmentsDeprecated'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($policyAssignmentsDeprecated).count) Policy assignments / deprecated built-in Policy
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Policy Assignment DisplayName
-Policy AssignmentId
-Policy/PolicySet
-PolicySet DisplayName
-PolicySetId
-Policy DisplayName
-PolicyId
-Deprecated Property
-
-
-
-"@)
- $htmlSUMMARYPolicyAssignmentsDeprecatedPolicy = $null
- $htmlSUMMARYPolicyAssignmentsDeprecatedPolicy = foreach ($policyAssignmentDeprecated in $policyAssignmentsDeprecated | Sort-Object @{Expression = { $_.PolicyAssignmentDisplayName } }, @{Expression = { $_.PolicyAssignmentId } }) {
- @"
-
-$($policyAssignmentDeprecated.PolicyAssignmentDisplayName -replace '<', '<' -replace '>', '>')
-$($policyAssignmentDeprecated.PolicyAssignmentId -replace '<', '<' -replace '>', '>')
-$($policyAssignmentDeprecated.PolicyType)
-$($policyAssignmentDeprecated.PolicySetDisplayName -replace '<', '<' -replace '>', '>')
-$($policyAssignmentDeprecated.PolicySetId -replace '<', '<' -replace '>', '>')
-$($policyAssignmentDeprecated.PolicyDisplayName -replace '<', '<' -replace '>', '>')
-$($policyAssignmentDeprecated.PolicyId -replace '<', '<' -replace '>', '>')
-$($policyAssignmentDeprecated.DeprecatedProperty)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYPolicyAssignmentsDeprecatedPolicy)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($policyAssignmentsDeprecated).count) Policy assignments / deprecated built-in Policy
-"@)
- }
- #endregion SUMMARYPolicyAssignmentsDeprecatedPolicy
- $endcustpolassdeprpol = Get-Date
- Write-Host " processing PolicyAssignmentsDeprecatedPolicy duration: $((New-TimeSpan -Start $startcustpolassdeprpol -End $endcustpolassdeprpol).TotalSeconds) seconds"
-
- #region SUMMARYPolicyExemptions
- Write-Host ' processing TenantSummary Policy exemptions'
- $policyExemptionsCount = ($htPolicyAssignmentExemptions.Keys).Count
-
- if ($policyExemptionsCount -gt 0) {
- $tfCount = $policyExemptionsCount
- $htmlTableId = 'TenantSummary_policyExemptions'
-
- $expiredExemptionsCount = ($htPolicyAssignmentExemptions.Keys | Where-Object { $htPolicyAssignmentExemptions.($_).exemption.properties.expiresOn -and $htPolicyAssignmentExemptions.($_).exemption.properties.expiresOn -lt (Get-Date).ToUniversalTime() } | Measure-Object).count
-
- [void]$htmlTenantSummary.AppendLine(@"
-
$($policyExemptionsCount) Policy exemptions | Expired: $($expiredExemptionsCount)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-Management Group Id
-Management Group Name
-SubscriptionId
-Subscription Name
-ResourceGroup
-ResourceName / ResourceType
-Exemption name
-Exemption description
-Category
-ExpiresOn (UTC)
-Exemption Id
-Policy AssignmentId
-Policy Type
-Policy
-Exempted Set Policies
-CreatedBy
-CreatedAt
-LastModifiedBy
-LastModifiedAt
-
-
-
-"@)
-
- $htmlSUMMARYPolicyExemptions = $null
- $exemptionData4CSVExport = [System.Collections.ArrayList]@()
- $htmlSUMMARYPolicyExemptions = foreach ($policyExemption in $htPolicyAssignmentExemptions.Keys | Sort-Object) {
- $exemption = $htPolicyAssignmentExemptions.$policyExemption.exemption
- if ($exemption.properties.expiresOn) {
- $exemptionExpiresOnFormated = (($exemption.properties.expiresOn))
- if ($exemption.properties.expiresOn -gt (Get-Date).ToUniversalTime()) {
- $exemptionExpiresOn = $exemptionExpiresOnFormated
- }
- else {
- $exemptionExpiresOn = "expired $($exemptionExpiresOnFormated)"
- }
- }
- else {
- $exemptionExpiresOn = 'n/a'
- }
-
- $splitExemptionId = ($exemption.Id).Split('/')
- if (($exemption.Id) -like '/subscriptions/*') {
-
- switch (($splitExemptionId).Count - 1) {
- #sub
- 6 {
- $exemptionScope = 'Sub'
- $subId = $splitExemptionId[2]
- $subdetails = $htSubDetails.($subId).details
- $mgId = $subdetails.MgId
- $mgName = $subdetails.MgName
- $subName = $subdetails.Subscription
- $rgName = ''
- $resName = ''
- }
-
- #rg
- 8 {
- $exemptionScope = 'RG'
- $subId = $splitExemptionId[2]
- $subdetails = $htSubDetails.($subId).details
- $mgId = $subdetails.MgId
- $mgName = $subdetails.MgName
- $subName = $subdetails.Subscription
- $rgName = $splitExemptionId[4]
- $resName = ''
- }
-
- #res
- 12 {
- $exemptionScope = 'Res'
- $subId = $splitExemptionId[2]
- $subdetails = $htSubDetails.($subId).details
- $mgId = $subdetails.MgId
- $mgName = $subdetails.MgName
- $subName = $subdetails.Subscription
- $rgName = $splitExemptionId[4]
- $resName = "$($splitExemptionId[8]) / $($splitExemptionId[6..7] -join '/')"
- }
- }
- }
- else {
- $exemptionScope = 'MG'
- $mgId = $splitExemptionId[4]
- $mgdetails = $htMgDetails.($mgId).details
- $mgName = $mgdetails.MgName
- $subId = ''
- $subName = ''
- $rgName = ''
- $resName = ''
- }
-
- $policyType = 'unknown'
- $policy = 'unknown'
- $arrayExemptedPolicies = @()
- $arrayExemptedPoliciesCSV = @()
- $policiesExempted = $null
- $policiesExemptedCSV = $null
- $policiesExemptedCSVCount = $null
- $policiesTotalCount = $null
- if ($htCacheAssignmentsPolicy.(($exemption.properties.policyAssignmentId).tolower()).Assignment.properties.policyDefinitionId) {
- $policyDefinitionId = $htCacheAssignmentsPolicy.(($exemption.properties.policyAssignmentId).tolower()).Assignment.properties.policyDefinitionId
-
- if ($policyDefinitionId -like '*/providers/Microsoft.Authorization/policyDefinitions/*') {
- $policyType = 'Policy'
- if ($htCacheDefinitionsPolicy.($policyDefinitionId.tolower())) {
- $policyDetail = $htCacheDefinitionsPolicy.($policyDefinitionId.tolower())
- if ($policyDetail.Type -eq 'BuiltIn') {
- $policy = $policyDetail.LinkToAzAdvertizer
- }
- else {
- $policy = "$($policyDetail.DisplayName) ($($policyDetail.Id))"
- }
- $policiesExempted = $null
- $policyClear = "$($policyDetail.DisplayName) ($($policyDetail.Id))"
- }
- }
-
- if ($policyDefinitionId -like '*/providers/Microsoft.Authorization/policySetDefinitions/*') {
- $policyType = 'PolicySet'
- if ($htCacheDefinitionsPolicySet.($policyDefinitionId.tolower())) {
- $policyDetail = $htCacheDefinitionsPolicySet.($policyDefinitionId.tolower())
- if ($policyDetail.Type -eq 'BuiltIn') {
- $policy = $policyDetail.LinkToAzAdvertizer
- }
- else {
- $policy = "$($policyDetail.DisplayName) ($($policyDetail.Id))"
- }
- $policiesTotalCount = $htCacheDefinitionsPolicySet.($policyDefinitionId.tolower()).PolicySetPolicyRefIds.Count
- if ($exemption.properties.policyDefinitionReferenceIds.Count -gt 0) {
- foreach ($exemptedRefId in $exemption.properties.policyDefinitionReferenceIds) {
- $policyExempted = 'unknown'
- $policyExemptedCSV = 'unknown'
- if ($htCacheDefinitionsPolicySet.($policyDefinitionId.tolower()).PolicySetPolicyRefIds.($exemptedRefId)) {
- $exemptedPolicyId = $htCacheDefinitionsPolicySet.($policyDefinitionId.tolower()).PolicySetPolicyRefIds.($exemptedRefId)
- if ($htCacheDefinitionsPolicy.($exemptedPolicyId.tolower())) {
- $policyExemptedDetail = $htCacheDefinitionsPolicy.($exemptedPolicyId.tolower())
- if ($policyExemptedDetail.Type -eq 'BuiltIn') {
- $policyExempted = $policyExemptedDetail.LinkToAzAdvertizer
- }
- else {
- $policyExempted = "$($policyExemptedDetail.DisplayName) ($($policyExemptedDetail.Id))"
- }
- $policyExemptedCSV = "$($policyExemptedDetail.DisplayName) ($($policyExemptedDetail.Id))"
-
- }
- }
- $arrayExemptedPolicies += $policyExempted
- $arrayExemptedPoliciesCSV += $policyExemptedCSV
- }
-
- $policiesExempted = "$($arrayExemptedPolicies.Count)/$($policiesTotalCount) ( $(($arrayExemptedPolicies | Sort-Object) -join ' '))"
- $policiesExemptedCSV = ($arrayExemptedPoliciesCSV | Sort-Object) -join "$CsvDelimiterOpposite "
- $policiesExemptedCSVCount = $arrayExemptedPoliciesCSV.Count
- }
- else {
- $policiesExempted = "all $policiesTotalCount"
- $policiesExemptedCSV = "all $policiesTotalCount"
- $policiesExemptedCSVCount = $policiesTotalCount
- }
-
- $policyClear = "$($policyDetail.DisplayName) ($($policyDetail.Id))"
- }
- }
-
- }
-
- if (-not $NoCsvExport) {
- $null = $exemptionData4CSVExport.Add([PSCustomObject]@{
- Scope = $exemptionScope
- ManagementGroupId = $mgId
- ManagementGroupName = $mgName
- SubscriptionId = $subId
- SubscriptionName = $subName
- ResourceGroup = $rgName
- ResourceName_ResourceType = $resName
- ExemptionName = $exemption.properties.DisplayName
- ExemptionDescription = $exemption.properties.Description
- Category = $exemption.properties.exemptionCategory
- ExpiresOn_UTC = $exemptionExpiresOn
- ExemptionId = $exemption.Id
- PolicyAssignmentId = $exemption.properties.policyAssignmentId
- PolicyType = $policyType
- Policy = $policyClear
- PoliciesTotalCount = $policiesTotalCount
- PoliciesExemptedCount = $policiesExemptedCSVCount
- PoliciesExempted = $policiesExemptedCSV
- CreatedBy = "$($exemption.systemData.createdBy) ($($exemption.systemData.createdByType))"
- CreatedAt = $exemption.systemData.createdAt.ToString('yyyy-MM-dd HH:mm:ss')
- LastModifiedBy = "$($exemption.systemData.lastModifiedBy) ($($exemption.systemData.lastModifiedByType))"
- LastModifiedAt = $exemption.systemData.lastModifiedAt.ToString('yyyy-MM-dd HH:mm:ss')
- })
- }
-
- @"
-
-$($exemptionScope)
-$($mgId)
-$($mgName -replace '<', '<' -replace '>', '>')
-$($subId)
-$($subName)
-$($rgName)
-$($resName)
-$($exemption.properties.DisplayName -replace '<', '<' -replace '>', '>')
-$($exemption.properties.Description -replace '<', '<' -replace '>', '>')
-$($exemption.properties.exemptionCategory -replace '<', '<' -replace '>', '>')
-$($exemptionExpiresOn)
-$($exemption.Id)
-$($exemption.properties.policyAssignmentId -replace '<', '<' -replace '>', '>')
-$($policyType)
-$($policy)
-$($policiesExempted)
-$($exemption.systemData.createdBy) ($($exemption.systemData.createdByType))
-$($exemption.systemData.createdAt.ToString('yyyy-MM-dd HH:mm:ss'))
-$($exemption.systemData.lastModifiedBy) ($($exemption.systemData.lastModifiedByType))
-$($exemption.systemData.lastModifiedAt.ToString('yyyy-MM-dd HH:mm:ss'))
-
-"@
- }
-
- if (-not $NoCsvExport) {
- Write-Host "Exporting PolicyExemptions CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName)_PolicyExemptions.csv'"
- $exemptionData4CSVExport | Sort-Object -Property PolicyAssignmentId, Id | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_PolicyExemptions.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
-
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYPolicyExemptions)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($policyExemptionsCount) Policy exemptions
-"@)
- }
- #endregion SUMMARYPolicyExemptions
-
- #region SUMMARYPolicyAssignmentsOrphaned
- Write-Host ' processing TenantSummary PolicyAssignments orphaned'
-
- if ($policyAssignmentsOrphanedCount -gt 0) {
- $tfCount = $policyAssignmentsOrphanedCount
- $htmlTableId = 'TenantSummary_policyAssignmentsOrphaned'
-
- [void]$htmlTenantSummary.AppendLine(@"
-
$($policyAssignmentsOrphanedCount) Policy assignments orphaned
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Policy AssignmentId
-Policy/Set definition
-
-
-
-"@)
-
- $htmlSUMMARYPolicyassignmentsOrphaned = $null
- $htmlSUMMARYPolicyassignmentsOrphaned = foreach ($orphanedPolicyAssignment in $policyAssignmentsOrphaned | Sort-Object -Property PolicyAssignmentId) {
- @"
-
-$($orphanedPolicyAssignment.policyAssignmentId -replace '<', '<' -replace '>', '>')
-$($orphanedPolicyAssignment.PolicyDefinitionId -replace '<', '<' -replace '>', '>')
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYPolicyassignmentsOrphaned)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($policyAssignmentsOrphanedCount) Policy assignments orphaned
-"@)
- }
- #endregion SUMMARYPolicyAssignmentsOrphaned
-
- #region SUMMARYPolicyAssignmentsAll
- $startSummaryPolicyAssignmentsAll = Get-Date
- $allPolicyAssignments = ($policyBaseQuery).count
- Write-Host " processing TenantSummary PolicyAssignments (all $allPolicyAssignments)"
-
- $script:arrayPolicyAssignmentsEnriched = [System.Collections.ArrayList]@()
- $cnter = 0
-
- #region PolicyAssignmentsRoleAssignmentMapping
- $startPolicyAssignmentsRoleAssignmentMapping = Get-Date
- Write-Host ' processing PolicyAssignmentsRoleAssignmentMapping'
- $script:htPolicyAssignmentRoleAssignmentMapping = @{}
- foreach ($roleassignmentId in ($htCacheAssignmentsRole).keys | Sort-Object) {
- $roleAssignment = ($htCacheAssignmentsRole).($roleassignmentId).Assignment
-
- if ($htManagedIdentityForPolicyAssignment.($roleAssignment.ObjectId)) {
- $mi = $htManagedIdentityForPolicyAssignment.($roleAssignment.ObjectId)
-
- #this
- if (-not $htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower())) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()) = @{}
- }
-
- if (($htCacheDefinitionsRole).($roleAssignment.RoleDefinitionId).IsCustom) {
- $roleDefinitionType = 'custom'
- }
- else {
- $roleDefinitionType = 'builtin'
- }
-
- $array = [System.Collections.ArrayList]@()
- $null = $array.Add([PSCustomObject]@{
- roleassignmentId = $roleassignmentId
- roleDefinitionId = $roleAssignment.RoleDefinitionId
- roleDefinitionName = $roleAssignment.RoleDefinitionName
- roleDefinitionType = $roleDefinitionType
- })
-
- #this
- if ($htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments += $array
- }
- else {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = $array
- }
- }
- }
-
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- foreach ($roleassignmentId in ($htCacheAssignmentsRBACOnResourceGroupsAndResources).keys | Sort-Object) {
- $roleAssignment = ($htCacheAssignmentsRBACOnResourceGroupsAndResources).($roleassignmentId)
-
- if ($htManagedIdentityForPolicyAssignment.($roleAssignment.ObjectId)) {
- $mi = $htManagedIdentityForPolicyAssignment.($roleAssignment.ObjectId)
-
- #this
- if (-not $htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower())) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()) = @{}
- }
-
- if (($htCacheDefinitionsRole).($roleAssignment.RoleDefinitionId).IsCustom) {
- $roleDefinitionType = 'custom'
- }
- else {
- $roleDefinitionType = 'builtin'
- }
-
- $array = [System.Collections.ArrayList]@()
- $null = $array.Add([PSCustomObject]@{
- roleassignmentId = $roleassignmentId
- roleDefinitionId = $roleAssignment.RoleDefinitionId
- roleDefinitionName = $roleAssignment.RoleDefinitionName
- roleDefinitionType = $roleDefinitionType
- })
-
- #this
- if ($htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments += $array
- }
- else {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = $array
- }
- }
- }
- }
- $htPolicyAssignmentRoleAssignmentMappingCount = ($htPolicyAssignmentRoleAssignmentMapping.keys).Count
- $endPolicyAssignmentsRoleAssignmentMapping = Get-Date
- Write-Host " PolicyAssignmentsRoleAssignmentMapping processing duration: $((New-TimeSpan -Start $startPolicyAssignmentsRoleAssignmentMapping -End $endPolicyAssignmentsRoleAssignmentMapping).TotalMinutes) minutes ($((New-TimeSpan -Start $startPolicyAssignmentsRoleAssignmentMapping -End $endPolicyAssignmentsRoleAssignmentMapping).TotalSeconds) seconds)"
- #endregion PolicyAssignmentsRoleAssignmentMapping
-
- #region PolicyAssignmentsUniqueRelations
- $startPolicyAssignmnetsUniqueRelations = Get-Date
- Write-Host ' processing PolicyAssignmnetsUniqueRelations'
- $htPolicyAssignmentRelatedRoleAssignments = @{}
- $htPolicyAssignmentRelatedExemptions = @{}
-
- foreach ($policyAssignmentIdUnique in $policyBaseQueryUniqueAssignments) {
-
- #region relatedRoleAssignments
- $relatedRoleAssignmentsArray = @()
- $relatedRoleAssignmentsArrayClear = @()
- if ($htPolicyAssignmentRoleAssignmentMappingCount -gt 0) {
- if ($htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId)) {
- foreach ($entry in $htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId).roleassignments) {
- if ($entry.roleDefinitionType -eq 'builtin') {
- $relatedRoleAssignmentsArray += "
$($entry.roleDefinitionName) ($($entry.roleAssignmentId))"
- }
- else {
- $relatedRoleAssignmentsArray += "
$($entry.roleDefinitionName -replace '<', '<' -replace '>', '>') ($($entry.roleAssignmentId))"
- }
- $relatedRoleAssignmentsArrayClear += "$($entry.roleDefinitionName) ($($entry.roleAssignmentId))"
- }
- }
- }
-
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId) = @{}
- if (($relatedRoleAssignmentsArray).count -gt 0) {
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignments = ($relatedRoleAssignmentsArray | Sort-Object) -join "$CsvDelimiterOpposite "
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignmentsClear = ($relatedRoleAssignmentsArrayClear | Sort-Object) -join "$CsvDelimiterOpposite "
- }
- else {
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignments = 'none'
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignmentsClear = 'none'
- }
- #endregion relatedRoleAssignments
-
- #region exemptions
- $arrayExemptions = @()
- foreach ($exemptionId in $htPolicyAssignmentExemptions.keys) {
- if ($htPolicyAssignmentExemptions.($exemptionId).exemption.properties.policyAssignmentId -eq $policyAssignmentIdUnique.PolicyAssignmentId) {
- $arrayExemptions += $htPolicyAssignmentExemptions.($exemptionId).exemption
- if (-not $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId)) {
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId) = @{}
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptionsCount = 1
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptions = $arrayExemptions
- }
- else {
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptionsCount += 1
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptions = $arrayExemptions
- }
- }
- }
- #endregion exemptions
- }
- $endPolicyAssignmnetsUniqueRelations = Get-Date
- Write-Host " PolicyAssignmnetsUniqueRelations processing duration: $((New-TimeSpan -Start $startPolicyAssignmnetsUniqueRelations -End $endPolicyAssignmnetsUniqueRelations).TotalMinutes) minutes ($((New-TimeSpan -Start $startPolicyAssignmnetsUniqueRelations -End $endPolicyAssignmnetsUniqueRelations).TotalSeconds) seconds)"
- #endregion PolicyAssignmentsUniqueRelations
-
- #region PolicyAssignmentsAllCreateEnriched
- $startPolicyAssignmentsAllCreateEnriched = Get-Date
- Write-Host ' processing PolicyAssignmentsAllCreateEnriched'
- foreach ($policyAssignmentAll in $policyBaseQuery) {
-
- $cnter++
- if ($cnter % 1000 -eq 0) {
- $etappeSummaryPolicyAssignmentsAll = Get-Date
- Write-Host " $cnter of $allPolicyAssignments PolicyAssignments processed: $((New-TimeSpan -Start $startSummaryPolicyAssignmentsAll -End $etappeSummaryPolicyAssignmentsAll).TotalSeconds) seconds"
- }
-
- #region AzAdvertizerLinkOrNot
- if ($policyAssignmentAll.PolicyType -eq 'builtin') {
- if ($policyAssignmentAll.PolicyVariant -eq 'Policy') {
- $azaLinkOrNot = "
$($policyAssignmentAll.Policy) "
- }
- else {
- $azaLinkOrNot = "
$($policyAssignmentAll.Policy) "
- }
- }
- else {
- $azaLinkOrNot = $policyAssignmentAll.Policy
- }
- #endregion AzAdvertizerLinkOrNot
-
- #region excludedScope
- $excludedScope = 'false'
- if (($policyAssignmentAll.PolicyAssignmentNotScopes).count -gt 0) {
- foreach ($policyAssignmentNotScope in $policyAssignmentAll.PolicyAssignmentNotScopes) {
- if (-not [String]::IsNullOrEmpty($policyAssignmentAll.subscriptionId)) {
- if ($htSubscriptionsMgPath.($policyAssignmentAll.subscriptionId).path -contains ($($policyAssignmentNotScope -replace '/subscriptions/' -replace '/providers/Microsoft.Management/managementGroups/'))) {
- $excludedScope = 'true'
- }
- }
- else {
- if ($htManagementGroupsMgPath.($policyAssignmentAll.MgId).path -contains ($($policyAssignmentNotScope -replace '/providers/Microsoft.Management/managementGroups/'))) {
- $excludedScope = 'true'
- }
- }
- }
- }
- #endregion excludedScope
-
- #region exemptions
- $exemptionScope = 'false'
- if ($htPolicyAssignmentRelatedExemptions.($policyAssignmentAll.PolicyAssignmentId)) {
- foreach ($exemption in $htPolicyAssignmentRelatedExemptions.($policyAssignmentAll.PolicyAssignmentId).exemptions) {
- if ($exemption.properties.expiresOn) {
- if ($exemption.properties.expiresOn -gt (Get-Date).ToUniversalTime()) {
- if (-not [String]::IsNullOrEmpty($policyAssignmentAll.subscriptionId)) {
- if ($htSubscriptionsMgPath.($policyAssignmentAll.subscriptionId).path -contains ($(($exemption.Id -split '/providers/Microsoft.Authorization/policyExemptions/')[0] -replace '/subscriptions/' -replace '/providers/Microsoft.Management/managementGroups/'))) {
- $exemptionScope = 'true'
- }
- }
- else {
- if ($htManagementGroupsMgPath.($policyAssignmentAll.MgId).path -contains ($(($exemption.Id -split '/providers/Microsoft.Authorization/policyExemptions/')[0] -replace '/subscriptions/' -replace '/providers/Microsoft.Management/managementGroups/'))) {
- $exemptionScope = 'true'
- }
- }
- }
- else {
- #Write-Host "$($exemption.Id) $($exemption.properties.expiresOn) $((Get-Date).ToUniversalTime()) expired"
- }
- }
- else {
- #same code as above / function?
- if (-not [String]::IsNullOrEmpty($policyAssignmentAll.subscriptionId)) {
- if ($htSubscriptionsMgPath.($policyAssignmentAll.subscriptionId).path -contains ($(($exemption.Id -split '/providers/Microsoft.Authorization/policyExemptions/')[0] -replace '/subscriptions/' -replace '/providers/Microsoft.Management/managementGroups/'))) {
- $exemptionScope = 'true'
- }
- }
- else {
- if ($htManagementGroupsMgPath.($policyAssignmentAll.MgId).path -contains ($(($exemption.Id -split '/providers/Microsoft.Authorization/policyExemptions/')[0] -replace '/subscriptions/' -replace '/providers/Microsoft.Management/managementGroups/'))) {
- $exemptionScope = 'true'
- }
- }
- }
- }
- }
- #endregion exemptions
-
- #region inheritance
- if ($policyAssignmentAll.PolicyAssignmentId -like '/providers/Microsoft.Management/managementGroups/*') {
- if (-not [String]::IsNullOrEmpty($policyAssignmentAll.SubscriptionId)) {
- $scope = "inherited $($policyAssignmentAll.PolicyAssignmentScope -replace '.*/')"
- }
- else {
- if (($policyAssignmentAll.PolicyAssignmentScope -replace '.*/') -eq $policyAssignmentAll.MgId) {
- $scope = 'thisScope Mg'
- }
- else {
- $scope = "inherited $($policyAssignmentAll.PolicyAssignmentScope -replace '.*/')"
- }
- }
- }
-
- if ($policyAssignmentAll.PolicyAssignmentId -like '/subscriptions/*' -and $policyAssignmentAll.PolicyAssignmentId -notlike '/subscriptions/*/resourcegroups/*') {
- $scope = 'thisScope Sub'
- }
-
- if ($policyAssignmentAll.PolicyAssignmentId -like '/subscriptions/*/resourcegroups/*') {
- $scope = 'thisScope Sub RG'
- }
- #endregion inheritance
-
- #region effect
- $effect = 'unknown'
- if ($policyAssignmentAll.PolicyVariant -eq 'Policy') {
-
- $test0 = $policyAssignmentAll.PolicyAssignmentParameters.effect.value
- if ($test0) {
- $effect = $test0
- }
- else {
- $test1 = $policyAssignmentAll.PolicyDefinitionEffectDefault
- if ($test1 -ne 'n/a') {
- $effect = $test1
- }
- $test2 = $policyAssignmentAll.PolicyDefinitionEffectFixed
- if ($test2 -ne 'n/a') {
- $effect = $test2
- }
- }
- }
- else {
- $effect = 'n/a'
- }
- #endregion effect
-
- #region mgOrSubOrRG
- if ([String]::IsNullOrEmpty($policyAssignmentAll.SubscriptionId)) {
- $mgOrSubOrRG = 'Mg'
- }
- else {
- if ($scope -like '*RG') {
- $mgOrSubOrRG = 'RG'
- }
- else {
- $mgOrSubOrRG = 'Sub'
- }
- }
- #endregion mgOrSubOrRG
-
- #region category
- if ([string]::IsNullOrEmpty($policyAssignmentAll.PolicyCategory)) {
- $policyCategory = 'n/a'
- }
- else {
- $policyCategory = $policyAssignmentAll.PolicyCategory
- }
- #endregion category
-
- #region createdByUpdatedBy
- #createdBy
- if ($policyAssignmentAll.PolicyAssignmentCreatedBy) {
- $createdBy = $policyAssignmentAll.PolicyAssignmentCreatedBy
- if ($htIdentitiesWithRoleAssignmentsUnique.($createdBy)) {
- $createdBy = $htIdentitiesWithRoleAssignmentsUnique.($createdBy).details
- }
- }
- else {
- $createdBy = ''
- }
-
- #UpdatedBy
- if ($policyAssignmentAll.PolicyAssignmentUpdatedBy) {
- $updatedBy = $policyAssignmentAll.PolicyAssignmentUpdatedBy
- if ($htIdentitiesWithRoleAssignmentsUnique.($updatedBy)) {
- $updatedBy = $htIdentitiesWithRoleAssignmentsUnique.($updatedBy).details
- }
- }
- else {
- $updatedBy = ''
- }
- #endregion createdByUpdatedBy
-
- #region policyAssignmentNotScopes
- if ($policyAssignmentAll.PolicyAssignmentNotScopes) {
- $policyAssignmentNotScopes = $policyAssignmentAll.PolicyAssignmentNotScopes -join $CsvDelimiterOpposite
- }
- else {
- $policyAssignmentNotScopes = 'n/a'
- }
- #endregion policyAssignmentNotScopes
-
- #region
- $policyAssignmentMI = ''
- if ($htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentAll.PolicyAssignmentId)) {
- $hlp = $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentAll.PolicyAssignmentId)
- $relatedRoleAssignments = $hlp.relatedRoleAssignments
- $relatedRoleAssignmentsClear = $hlp.relatedRoleAssignmentsClear
- if ($htManagedIdentityDisplayName.("$($policyAssignmentAll.PolicyAssignmentId -replace '.*/')_$($policyAssignmentAll.PolicyAssignmentId)")) {
- $hlp = $htManagedIdentityDisplayName.("$($policyAssignmentAll.PolicyAssignmentId -replace '.*/')_$($policyAssignmentAll.PolicyAssignmentId)")
- $policyAssignmentMI = "$($hlp.displayname) (SPObjId: $($hlp.id))"
- }
- }
- #endregion
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- #region policyCompliance
- $policyAssignmentIdToLower = ($policyAssignmentAll.policyAssignmentId).ToLower()
-
- #mg
- if ([String]::IsNullOrEmpty($policyAssignmentAll.subscriptionId)) {
- if (($htCachePolicyComplianceResponseTooLargeMG).($policyAssignmentAll.MgId)) {
- $NonCompliantPolicies = 'skipped'
- $CompliantPolicies = 'skipped'
- $NonCompliantResources = 'skipped'
- $CompliantResources = 'skipped'
- $ConflictingResources = 'skipped'
- }
- else {
- $compliance = ($htCachePolicyComplianceMG).($policyAssignmentAll.MgId).($policyAssignmentIdToLower)
- $NonCompliantPolicies = $compliance.NonCompliantPolicies
- $CompliantPolicies = $compliance.CompliantPolicies
- $NonCompliantResources = $compliance.NonCompliantResources
- $CompliantResources = $compliance.CompliantResources
- $ConflictingResources = $compliance.ConflictingResources
-
- if (!$NonCompliantPolicies) {
- $NonCompliantPolicies = 0
- }
- if (!$CompliantPolicies) {
- $CompliantPolicies = 0
- }
- if (!$NonCompliantResources) {
- $NonCompliantResources = 0
- }
- if (!$CompliantResources) {
- $CompliantResources = 0
- }
- if (!$ConflictingResources) {
- $ConflictingResources = 0
- }
- }
- }
-
- #sub/rg
- if (-not [String]::IsNullOrEmpty($policyAssignmentAll.subscriptionId)) {
- if (($htCachePolicyComplianceResponseTooLargeSUB).($policyAssignmentAll.SubscriptionId)) {
- $NonCompliantPolicies = 'skipped'
- $CompliantPolicies = 'skipped'
- $NonCompliantResources = 'skipped'
- $CompliantResources = 'skipped'
- $ConflictingResources = 'skipped'
- }
- else {
- $compliance = ($htCachePolicyComplianceSUB).($policyAssignmentAll.SubscriptionId).($policyAssignmentIdToLower)
- $NonCompliantPolicies = $compliance.NonCompliantPolicies
- $CompliantPolicies = $compliance.CompliantPolicies
- $NonCompliantResources = $compliance.NonCompliantResources
- $CompliantResources = $compliance.CompliantResources
- $ConflictingResources = $compliance.ConflictingResources
-
- if (!$NonCompliantPolicies) {
- $NonCompliantPolicies = 0
- }
- if (!$CompliantPolicies) {
- $CompliantPolicies = 0
- }
- if (!$NonCompliantResources) {
- $NonCompliantResources = 0
- }
- if (!$CompliantResources) {
- $CompliantResources = 0
- }
- if (!$ConflictingResources) {
- $ConflictingResources = 0
- }
- }
- }
- #endregion policyCompliance
-
- $null = $script:arrayPolicyAssignmentsEnriched.Add([PSCustomObject]@{
- Level = $policyAssignmentAll.Level
- MgId = $policyAssignmentAll.MgId
- MgName = $policyAssignmentAll.MgName
- MgParentId = $policyAssignmentAll.MgParentId
- MgParentName = $policyAssignmentAll.MgParentName
- subscriptionId = $policyAssignmentAll.SubscriptionId
- subscriptionName = $policyAssignmentAll.Subscription
- PolicyAssignmentId = (($policyAssignmentAll.PolicyAssignmentId).ToLower())
- PolicyAssignmentScopeName = $policyAssignmentAll.PolicyAssignmentScopeName
- PolicyAssignmentDisplayName = $policyAssignmentAll.PolicyAssignmentDisplayName
- PolicyAssignmentDescription = $policyAssignmentAll.PolicyAssignmentDescription
- PolicyAssignmentEnforcementMode = $policyAssignmentAll.PolicyAssignmentEnforcementMode
- PolicyAssignmentNonComplianceMessages = $policyAssignmentAll.PolicyAssignmentNonComplianceMessages
- PolicyAssignmentNotScopes = $policyAssignmentNotScopes
- PolicyAssignmentParameters = $policyAssignmentAll.PolicyAssignmentParametersFormated
- PolicyAssignmentMI = $policyAssignmentMI
- AssignedBy = $policyAssignmentAll.PolicyAssignmentAssignedBy
- CreatedOn = $policyAssignmentAll.PolicyAssignmentCreatedOn
- CreatedBy = $createdBy
- UpdatedOn = $policyAssignmentAll.PolicyAssignmentUpdatedOn
- UpdatedBy = $updatedBy
- Effect = $effect
- PolicyName = $azaLinkOrNot
- PolicyNameClear = $policyAssignmentAll.Policy
- PolicyAvailability = $policyAssignmentAll.PolicyAvailability
- PolicyDescription = $policyAssignmentAll.PolicyDescription
- PolicyId = $policyAssignmentAll.PolicyDefinitionId
- PolicyVariant = $policyAssignmentAll.PolicyVariant
- PolicyType = $policyAssignmentAll.PolicyType
- PolicyIsALZ = $policyAssignmentAll.PolicyIsALZ
- PolicyCategory = $policyCategory
- Inheritance = $scope
- ExcludedScope = $excludedScope
- RelatedRoleAssignments = $relatedRoleAssignments
- RelatedRoleAssignmentsClear = $relatedRoleAssignmentsClear
- mgOrSubOrRG = $mgOrSubOrRG
- NonCompliantPolicies = $NonCompliantPolicies
- CompliantPolicies = $CompliantPolicies
- NonCompliantResources = $NonCompliantResources
- CompliantResources = $CompliantResources
- ConflictingResources = $ConflictingResources
- ExemptionScope = $exemptionScope
- })
- }
- else {
- $null = $script:arrayPolicyAssignmentsEnriched.Add([PSCustomObject]@{
- Level = $policyAssignmentAll.Level
- MgId = $policyAssignmentAll.MgId
- MgName = $policyAssignmentAll.MgName
- MgParentId = $policyAssignmentAll.MgParentId
- MgParentName = $policyAssignmentAll.MgParentName
- subscriptionId = $policyAssignmentAll.SubscriptionId
- subscriptionName = $policyAssignmentAll.Subscription
- PolicyAssignmentId = (($policyAssignmentAll.PolicyAssignmentId).ToLower())
- PolicyAssignmentScopeName = $policyAssignmentAll.PolicyAssignmentScopeName
- PolicyAssignmentDisplayName = $policyAssignmentAll.PolicyAssignmentDisplayName
- PolicyAssignmentDescription = $policyAssignmentAll.PolicyAssignmentDescription
- PolicyAssignmentEnforcementMode = $policyAssignmentAll.PolicyAssignmentEnforcementMode
- PolicyAssignmentNonComplianceMessages = $policyAssignmentAll.PolicyAssignmentNonComplianceMessages
- PolicyAssignmentNotScopes = $policyAssignmentNotScopes
- PolicyAssignmentParameters = $policyAssignmentAll.PolicyAssignmentParametersFormated
- PolicyAssignmentMI = $policyAssignmentMI
- AssignedBy = $policyAssignmentAll.PolicyAssignmentAssignedBy
- CreatedOn = $policyAssignmentAll.PolicyAssignmentCreatedOn
- CreatedBy = $createdBy
- UpdatedOn = $policyAssignmentAll.PolicyAssignmentUpdatedOn
- UpdatedBy = $updatedBy
- Effect = $effect
- PolicyName = $azaLinkOrNot
- PolicyNameClear = $policyAssignmentAll.Policy
- PolicyAvailability = $policyAssignmentAll.PolicyAvailability
- PolicyDescription = $policyAssignmentAll.PolicyDescription
- PolicyId = $policyAssignmentAll.PolicyDefinitionId
- PolicyVariant = $policyAssignmentAll.PolicyVariant
- PolicyType = $policyAssignmentAll.PolicyType
- PolicyIsALZ = $policyAssignmentAll.PolicyIsALZ
- PolicyCategory = $policyCategory
- Inheritance = $scope
- ExcludedScope = $excludedScope
- RelatedRoleAssignments = $relatedRoleAssignments
- RelatedRoleAssignmentsClear = $relatedRoleAssignmentsClear
- mgOrSubOrRG = $mgOrSubOrRG
- ExemptionScope = $exemptionScope
- })
- }
- }
- $EndPolicyAssignmentsAllCreateEnriched = Get-Date
- Write-Host " PolicyAssignmentsAllCreateEnriched processing duration: $((New-TimeSpan -Start $startPolicyAssignmentsAllCreateEnriched -End $EndPolicyAssignmentsAllCreateEnriched).TotalMinutes) minutes ($((New-TimeSpan -Start $startPolicyAssignmentsAllCreateEnriched -End $EndPolicyAssignmentsAllCreateEnriched).TotalSeconds) seconds)"
- #endregion PolicyAssignmentsAllCreateEnriched
-
- #region PolicyAssignmentsAllResolveIdentities
- Write-Host ' processing unresoved Identities (createdBy/updatedBy)'
- $startUnResolvedIdentitiesCreatedByUpdatedByPolicy = Get-Date
-
- $createdByNotResolved = ($arrayPolicyAssignmentsEnriched.where( { -not [string]::IsNullOrEmpty($_.CreatedBy) -and $_.CreatedBy -notlike 'ObjectType:*' })).CreatedBy | Sort-Object -Unique
- $updatedByNotResolved = ($arrayPolicyAssignmentsEnriched.where( { -not [string]::IsNullOrEmpty($_.UpdatedBy) -and $_.UpdatedBy -notlike 'ObjectType:*' })).UpdatedBy | Sort-Object -Unique
-
- $htNonResolvedIdentitiesPolicy = @{}
- foreach ($createdByNotResolvedEntry in $createdByNotResolved) {
- if (-not $htNonResolvedIdentitiesPolicy.($createdByNotResolvedEntry)) {
- $htNonResolvedIdentitiesPolicy.($createdByNotResolvedEntry) = @{}
- }
- }
- foreach ($updatedByNotResolvedEntry in $updatedByNotResolved) {
- if (-not $htNonResolvedIdentitiesPolicy.($updatedByNotResolvedEntry)) {
- $htNonResolvedIdentitiesPolicy.($updatedByNotResolvedEntry) = @{}
- }
- }
-
- $htNonResolvedIdentitiesPolicyCount = $htNonResolvedIdentitiesPolicy.Count
- if ($htNonResolvedIdentitiesPolicyCount -gt 0) {
- Write-Host " $htNonResolvedIdentitiesPolicyCount unresolved identities that created/updated a Policy assignment (createdBy/updatedBy)"
- $arrayUnresolvedIdentities = @()
- $arrayUnresolvedIdentities = foreach ($unresolvedIdentity in $htNonResolvedIdentitiesPolicy.keys) {
- if (-not [string]::IsNullOrEmpty($unresolvedIdentity)) {
- $unresolvedIdentity
- }
- }
- $arrayUnresolvedIdentitiesCount = $arrayUnresolvedIdentities.Count
- Write-Host " $arrayUnresolvedIdentitiesCount unresolved identities that have a value"
- if ($arrayUnresolvedIdentitiesCount.Count -gt 0) {
- $counterBatch = [PSCustomObject] @{ Value = 0 }
- $batchSize = 1000
- $ObjectBatch = $arrayUnresolvedIdentities | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
- $ObjectBatchCount = ($ObjectBatch | Measure-Object).Count
- $batchCnt = 0
-
- $script:htResolvedIdentitiesPolicy = @{}
-
- foreach ($batch in $ObjectBatch) {
- $batchCnt++
-
- $nonResolvedIdentitiesToCheck = '"{0}"' -f ($batch.Group.where({ testGuid $_ }) -join '","')
- Write-Host " IdentitiesToCheck: Batch #$batchCnt/$($ObjectBatchCount) ($(($batch.Group).Count))"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/directoryObjects/getByIds"
- $method = 'POST'
- $body = @"
- {
- "ids":[$($nonResolvedIdentitiesToCheck)]
- }
-"@
-
- function resolveIdentitiesPolicy($currentTask) {
- $resolvedIdentities = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -currentTask $currentTask
- $resolvedIdentitiesCount = $resolvedIdentities.Count
- Write-Host " $resolvedIdentitiesCount identities resolved"
- if ($resolvedIdentitiesCount -gt 0) {
- foreach ($resolvedIdentity in $resolvedIdentities) {
- if (-not $htResolvedIdentitiesPolicy.($resolvedIdentity.id)) {
- $script:htResolvedIdentitiesPolicy.($resolvedIdentity.id) = @{}
- if ($resolvedIdentity.'@odata.type' -eq '#microsoft.graph.servicePrincipal') {
- if ($resolvedIdentity.servicePrincipalType -eq 'ManagedIdentity') {
- $miType = 'unknown'
- foreach ($altName in $resolvedIdentity.alternativeNames) {
- if ($altName -like 'isExplicit=*') {
- $splitAltName = $altName.split('=')
- if ($splitAltName[1] -eq 'true') {
- $miType = 'Usr'
- }
- if ($splitAltName[1] -eq 'false') {
- $miType = 'Sys'
- }
- }
- }
- $sptype = "MI $miType"
- $custObjectType = "ObjectType: SP $sptype, ObjectDisplayName: $($resolvedIdentity.displayName), ObjectSignInName: n/a, ObjectId: $($resolvedIdentity.id) (rp)"
- }
- else {
- if ($resolvedIdentity.servicePrincipalType -eq 'Application') {
- $sptype = 'App'
- if ($resolvedIdentity.appOwnerOrganizationId -eq $azAPICallConf['checkContext'].Tenant.Id) {
- $custObjectType = "ObjectType: SP $sptype INT, ObjectDisplayName: $($resolvedIdentity.displayName), ObjectSignInName: n/a, ObjectId: $($resolvedIdentity.id) (rp)"
- }
- else {
- $custObjectType = "ObjectType: SP $sptype EXT, ObjectDisplayName: $($resolvedIdentity.displayName), ObjectSignInName: n/a, ObjectId: $($resolvedIdentity.id) (rp)"
- }
- }
- else {
- Write-Host "* * * Unexpected IdentityType $($resolvedIdentity.servicePrincipalType)"
- }
- }
- $script:htResolvedIdentitiesPolicy.($resolvedIdentity.id).custObjectType = $custObjectType
- $script:htResolvedIdentitiesPolicy.($resolvedIdentity.id).obj = $resolvedIdentity
- }
-
- if ($resolvedIdentity.'@odata.type' -eq '#microsoft.graph.user') {
- if ($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData) {
- $hlpObjectDisplayName = 'scrubbed'
- $hlpObjectSigninName = 'scrubbed'
- }
- else {
- $hlpObjectDisplayName = $resolvedIdentity.displayName
- $hlpObjectSigninName = $resolvedIdentity.userPrincipalName
- }
- $custObjectType = "ObjectType: User, ObjectDisplayName: $hlpObjectDisplayName, ObjectSignInName: $hlpObjectSigninName, ObjectId: $($resolvedIdentity.id) (rp)"
-
- $script:htResolvedIdentitiesPolicy.($resolvedIdentity.id).custObjectType = $custObjectType
- $script:htResolvedIdentitiesPolicy.($resolvedIdentity.id).obj = $resolvedIdentity
- }
-
- if ($resolvedIdentity.'@odata.type' -ne '#microsoft.graph.user' -and $resolvedIdentity.'@odata.type' -ne '#microsoft.graph.servicePrincipal') {
- Write-Host "!!! * * * IdentityType '$($resolvedIdentity.'@odata.type')' was not considered by Azure Governance Visualizer - if you see this line, please file an issue on GitHub - thank you." -ForegroundColor Yellow
- }
- }
- }
- }
- }
- resolveIdentitiesPolicy -currentTask 'resolveObjectbyId PolicyAssignment #1'
- }
-
- foreach ($policyAssignment in $script:arrayPolicyAssignmentsEnriched.where( { -not [string]::IsNullOrEmpty($_.CreatedBy) -and $_.CreatedBy -notlike 'ObjectType*' })) {
- if ($htResolvedIdentitiesPolicy.($policyAssignment.CreatedBy)) {
- $policyAssignment.CreatedBy = $htResolvedIdentitiesPolicy.($policyAssignment.CreatedBy).custObjectType
- }
- }
-
- foreach ($policyAssignment in $script:arrayPolicyAssignmentsEnriched.where( { -not [string]::IsNullOrEmpty($_.UpdatedBy) -and $_.UpdatedBy -notlike 'ObjectType*' })) {
- if ($htResolvedIdentitiesPolicy.($policyAssignment.UpdatedBy)) {
- $policyAssignment.UpdatedBy = $htResolvedIdentitiesPolicy.($policyAssignment.UpdatedBy).custObjectType
- }
- }
- }
- }
-
- $endUnResolvedIdentitiesCreatedByUpdatedByPolicy = Get-Date
- Write-Host " UnresolvedIdentities (createdBy/updatedBy) duration: $((New-TimeSpan -Start $startUnResolvedIdentitiesCreatedByUpdatedByPolicy -End $endUnResolvedIdentitiesCreatedByUpdatedByPolicy).TotalMinutes) minutes ($((New-TimeSpan -Start $startUnResolvedIdentitiesCreatedByUpdatedByPolicy -End $endUnResolvedIdentitiesCreatedByUpdatedByPolicy).TotalSeconds) seconds)"
- #endregion PolicyAssignmentsAllResolveIdentities
-
- $script:arrayPolicyAssignmentsEnrichedGroupedBySubscription = $arrayPolicyAssignmentsEnriched | Group-Object -Property subscriptionId
- $script:arrayPolicyAssignmentsEnrichedGroupedByManagementGroup = $arrayPolicyAssignmentsEnriched | Group-Object -Property MgId
-
- #region policyAssignmentsAllHTML
- Write-Host ' processing SummaryPolicyAssignmentsAllHTML'
- $startSummaryPolicyAssignmentsAllHTML = Get-Date
- if (($arrayPolicyAssignmentsEnriched).count -gt 0) {
-
- if (-not $NoCsvExport) {
- $csvFilename = "$($filename)_PolicyAssignments"
- Write-Host " Exporting PolicyAssignments CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
- if ($CsvExportUseQuotesAsNeeded) {
- $arrayPolicyAssignmentsEnriched | Sort-Object -Property Level, MgId, SubscriptionId, PolicyAssignmentId | Select-Object -ExcludeProperty PolicyName, RelatedRoleAssignments | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter "$csvDelimiter" -NoTypeInformation -UseQuotes AsNeeded
- }
- else {
- $arrayPolicyAssignmentsEnriched | Sort-Object -Property Level, MgId, SubscriptionId, PolicyAssignmentId | Select-Object -ExcludeProperty PolicyName, RelatedRoleAssignments | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
- }
-
- $policyAssignmentsUniqueCount = ($arrayPolicyAssignmentsEnriched | Sort-Object -Property PolicyAssignmentId -Unique).count
- if ($azAPICallConf['htParameters'].LargeTenant -or $azAPICallConf['htParameters'].PolicyAtScopeOnly) {
- $policyAssignmentsCount = $policyAssignmentsUniqueCount
- $tfCount = $policyAssignmentsCount
- }
- else {
- $policyAssignmentsCount = ($arrayPolicyAssignmentsEnriched).count
- $tfCount = $policyAssignmentsCount
- }
-
- if ($tfCount -gt $HtmlTableRowsLimit) {
- Write-Host " !Skipping TenantSummary PolicyAssignments HTML processing as $tfCount lines is exceeding the critical rows limit of $HtmlTableRowsLimit" -ForegroundColor Yellow
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($policyAssignmentsCount) Policy assignments ($policyAssignmentsUniqueCount unique)
-
-
-
Output of $tfCount lines would exceed the html rows limit of $HtmlTableRowsLimit (html file potentially would become unresponsive). Work with the CSV file $($csvFilename).csv | Note: the CSV file will only exist if you did NOT use parameter -NoCsvExport
-
You can adjust the html row limit by using parameter -HtmlTableRowsLimit
-
You can reduce the number of lines by using parameter -LargeTenant and/or -DoNotIncludeResourceGroupsAndResourcesOnRBAC
-
Check the parameters documentation Azure Governance Visualizer docs
-
-"@)
- }
- else {
-
- $htmlTableId = 'TenantSummary_policyAssignmentsAll'
- $noteOrNot = ''
-
- [void]$htmlTenantSummary.AppendLine(@"
-
$($policyAssignmentsCount) Policy assignments ($policyAssignmentsUniqueCount unique)
-
-
Download CSV
semicolon |
comma
-
*Depending on the number of rows and your computer´s performance the table may respond with delay, download the csv for better filtering experience
-
-
-
-Scope
-Management Group Id
-Management Group Name
-SubscriptionId
-Subscription Name
-Inheritance
-ScopeExcluded
-Exemption applies
-Policy/Set DisplayName
-Policy/Set Description
-Policy/SetId
-Policy/Set
-Type
-Category
-ALZ
-Effect
-Parameters
-Enforcement
-NonCompliance Message
-"@)
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- [void]$htmlTenantSummary.AppendLine(@'
-Policies NonCmplnt
-Policies Compliant
-Resources NonCmplnt
-Resources Compliant
-Resources Conflicting
-'@)
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-Role/Assignment $noteOrNot
-Managed Identity
-Assignment DisplayName
-Assignment Description
-AssignmentId
-AssignedBy
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
-
- $htmlTenantSummary | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $htmlTenantSummary = [System.Text.StringBuilder]::new()
- $htmlSummaryPolicyAssignmentsAll = $null
- $startloop = Get-Date
-
- $htmlSummaryPolicyAssignmentsAll = foreach ($policyAssignment in $arrayPolicyAssignmentsEnriched | Sort-Object -Property Level, MgName, MgId, SubscriptionName, SubscriptionId, PolicyAssignmentId) {
- if ($azAPICallConf['htParameters'].LargeTenant -or $azAPICallConf['htParameters'].PolicyAtScopeOnly) {
- if ($policyAssignment.Inheritance -like 'inherited *' -and $policyAssignment.MgParentId -ne "'upperScopes'") {
- continue
- }
- }
- if ($policyAssignment.PolicyType -eq 'Custom') {
- $policyName = ($policyAssignment.PolicyName -replace '<', '<' -replace '>', '>')
- }
- else {
- $policyName = $policyAssignment.PolicyName
- }
- @"
-
-$($policyAssignment.mgOrSubOrRG)
-$($policyAssignment.MgId)
-$($policyAssignment.MgName -replace '<', '<' -replace '>', '>')
-$($policyAssignment.SubscriptionId)
-$($policyAssignment.SubscriptionName)
-$($policyAssignment.Inheritance)
-$($policyAssignment.ExcludedScope)
-$($policyAssignment.ExemptionScope)
-$($policyName)
-$($policyAssignment.PolicyDescription -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyId -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyVariant)
-$($policyAssignment.PolicyType)
-$($policyAssignment.PolicyCategory -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyIsALZ)
-$($policyAssignment.Effect)
-$($policyAssignment.PolicyAssignmentParameters)
-$($policyAssignment.PolicyAssignmentEnforcementMode)
-$($policyAssignment.PolicyAssignmentNonComplianceMessages -replace '<', '<' -replace '>', '>')
-"@
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- @"
-$($policyAssignment.NonCompliantPolicies)
-$($policyAssignment.CompliantPolicies)
-$($policyAssignment.NonCompliantResources)
-$($policyAssignment.CompliantResources)
-$($policyAssignment.ConflictingResources)
-"@
- }
-
- @"
-$($policyAssignment.RelatedRoleAssignments)
-$($policyAssignment.PolicyAssignmentMI)
-$($policyAssignment.PolicyAssignmentDisplayName -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyAssignmentDescription -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyAssignmentId -replace '<', '<' -replace '>', '>')
-$($policyAssignment.AssignedBy)
-$($policyAssignment.CreatedOn)
-$($policyAssignment.CreatedBy)
-$($policyAssignment.UpdatedOn)
-$($policyAssignment.UpdatedBy)
-
-"@
- }
-
- $endloop = Get-Date
- Write-Host " html foreach loop duration: $((New-TimeSpan -Start $startloop -End $endloop).TotalSeconds) seconds"
-
- $start = Get-Date
- [void]$htmlTenantSummary.AppendLine($htmlSummaryPolicyAssignmentsAll)
- $htmlTenantSummary | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $htmlTenantSummary = [System.Text.StringBuilder]::new()
- $end = Get-Date
- Write-Host " html append file duration: $((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds"
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arrayPolicyAssignmentsEnriched).count) Policy assignments
-"@)
- }
- $endSummaryPolicyAssignmentsAllHTML = Get-Date
- Write-Host " SummaryPolicyAssignmentsAllHTML duration: $((New-TimeSpan -Start $startSummaryPolicyAssignmentsAllHTML -End $endSummaryPolicyAssignmentsAllHTML).TotalMinutes) minutes ($((New-TimeSpan -Start $startSummaryPolicyAssignmentsAllHTML -End $endSummaryPolicyAssignmentsAllHTML).TotalSeconds) seconds)"
- #endregion policyAssignmentsAllHTML
- $endSummaryPolicyAssignmentsAll = Get-Date
- Write-Host " SummaryPolicyAssignmentsAll duration: $((New-TimeSpan -Start $startSummaryPolicyAssignmentsAll -End $endSummaryPolicyAssignmentsAll).TotalMinutes) minutes ($((New-TimeSpan -Start $startSummaryPolicyAssignmentsAll -End $endSummaryPolicyAssignmentsAll).TotalSeconds) seconds)"
- #endregion SUMMARYPolicyAssignmentsAll
-
- #region SUMMARYPolicyRemediation
- Write-Host ' processing TenantSummary Policy Remediation'
-
- if ($arrayRemediatable.Count -gt 0) {
- $tfCount = $arrayRemediatable.Count
- $nonCompliantResourcesTotal = ($arrayRemediatable.nonCompliantResourcesCount | Measure-Object -Sum).Sum
- $htmlTableId = 'TenantSummary_PolicyRemediation'
- [void]$htmlTenantSummary.AppendLine(@"
-
$($arrayRemediatable.Count) Policies to remediate ($($nonCompliantResourcesTotal) nonCompliant resources)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Assignment Scope Type
-Assignment Scope
-Assignment Id
-Assignment DisplayName
-Assignment Policy/Set
-Effect
-Policy definition id
-Policy definition displayName
-Policy definition type
-Policy definition refId
-PolicySet definition id
-PolicySet definition displayName
-PolicySet definition type
-NonCompliant resources
-
-
-
-"@)
-
- $htmlSUMMARYPolicyRemediation = $null
- $arrayRemediatableSorted = $arrayRemediatable | Sort-Object -Property nonCompliantResourcesCount, policySetPolicyDefinitionReferenceId, policyDefinitionId, policyAssignmentId -Descending
- if (-not $NoCsvExport) {
- $csvFilename = "$($filename)_PolicyRemediation"
- Write-Host " Exporting PolicyRemediation CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
- $arrayRemediatableSorted | Export-Csv -Encoding utf8 -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter $csvDelimiter -NoTypeInformation
- }
- $htmlSUMMARYPolicyRemediation = foreach ($entry in $arrayRemediatableSorted) {
-
- if ($entry.policyDefinitionType -eq 'builtin') {
- $pd = "$($entry.policyDefinitionDisplayName) ($($entry.policyDefinitionName))"
- }
- else {
- $pd = "$($entry.policyDefinitionDisplayName) ($($entry.policyDefinitionName))"
- }
-
- if ($entry.policySetDefinitionType -ne 'n/a') {
- if ($entry.policySetDefinitionType -eq 'builtIn') {
- $psd = "$($entry.policySetDefinitionDisplayName) ($($entry.policySetDefinitionName))"
- }
- else {
- $psd = "$($entry.policySetDefinitionDisplayName) ($($entry.policySetDefinitionName))"
- }
- }
- else {
- $psd = $entry.policySetDefinitionType
- }
-
- @"
-
-$($entry.policyAssignmentScopeType)
-$($entry.policyAssignmentScope)
-$($entry.policyAssignmentId)
-$($entry.policyAssignmentDisplayName)
-$($entry.policyAssignmentPolicyOrPolicySet)
-$($entry.effect)
-$($entry.policyDefinitionId)
-$($pd)
-$($entry.policyDefinitionType)
-$($entry.policySetPolicyDefinitionReferenceId)
-$($entry.policySetDefinitionId)
-$($psd)
-$($entry.policySetDefinitionType)
-$($entry.nonCompliantResourcesCount)
-
-"@
- }
-
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYPolicyRemediation)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No Policies to remediate
-'@)
- }
- #endregion SUMMARYPolicyRemediation
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- #endregion tenantSummaryPolicy
-
- showMemoryUsage
-
- #region tenantSummaryRBAC
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-'@)
-
- #region SUMMARYtenanttotalcustomroles
- Write-Host ' processing TenantSummary Custom Roles'
- if ($tenantCustomRolesCount -gt $LimitRBACCustomRoleDefinitionsTenant * ($LimitCriticalPercentage / 100)) {
- $faimage = "
"
- }
- else {
- $faimage = "
"
- }
-
- if ($tenantCustomRolesCount -gt 0) {
- $tfCount = $tenantCustomRolesCount
- $htmlTableId = 'TenantSummary_customRoles'
- [void]$htmlTenantSummary.AppendLine(@"
-
$faimage $tenantCustomRolesCount Custom Role definitions ($scopeNamingSummary) (Limit: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant)
-
-
Download CSV
semicolon |
comma
-
-
-
-Role Name
-RoleId
-Assignable Scopes
-Data
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlSUMMARYtenanttotalcustomroles = $null
- $htmlSUMMARYtenanttotalcustomroles = foreach ($tenantCustomRole in $tenantCustomRoles | Sort-Object @{Expression = { $_.Name } }, @{Expression = { $_.Id } }) {
- $cachedTenantCustomRole = ($htCacheDefinitionsRole).($tenantCustomRole.Id)
- if (-not [string]::IsNullOrEmpty($cachedTenantCustomRole.DataActions) -or -not [string]::IsNullOrEmpty($cachedTenantCustomRole.NotDataActions)) {
- $roleManageData = 'true'
- }
- else {
- $roleManageData = 'false'
- }
-
- if (-not [string]::IsNullOrEmpty($cachedTenantCustomRole.Json.properties.createdBy)) {
- $createdBy = $cachedTenantCustomRole.Json.properties.createdBy
- if ($htIdentitiesWithRoleAssignmentsUnique.($createdBy)) {
- $createdBy = $htIdentitiesWithRoleAssignmentsUnique.($createdBy).details
- }
- }
- else {
- $createdBy = 'IsNullOrEmpty'
- }
-
- $createdOn = $cachedTenantCustomRole.Json.properties.createdOn
- $createdOnFormated = $createdOn
- $updatedOn = $cachedTenantCustomRole.Json.properties.updatedOn
- if ($updatedOn -eq $createdOn) {
- $updatedOnFormated = ''
- $updatedByRemoveNoiseOrNot = ''
- }
- else {
- $updatedOnFormated = $updatedOn
- if (-not [string]::IsNullOrEmpty($cachedTenantCustomRole.Json.properties.updatedBy)) {
- $updatedByRemoveNoiseOrNot = $cachedTenantCustomRole.Json.properties.updatedBy
- if ($htIdentitiesWithRoleAssignmentsUnique.($updatedByRemoveNoiseOrNot)) {
- $updatedByRemoveNoiseOrNot = $htIdentitiesWithRoleAssignmentsUnique.($updatedByRemoveNoiseOrNot).details
- }
- }
- else {
- $updatedByRemoveNoiseOrNot = 'IsNullOrEmpty'
- }
- }
- @"
-
-$($cachedTenantCustomRole.Name -replace '<', '<' -replace '>', '>')
-$($cachedTenantCustomRole.Id)
-$(($cachedTenantCustomRole.AssignableScopes).count) ($($cachedTenantCustomRole.AssignableScopes -join "$CsvDelimiterOpposite "))
-$($roleManageData)
-$createdOnFormated
-$createdBy
-$updatedOnFormated
-$updatedByRemoveNoiseOrNot
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYtenanttotalcustomroles)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$tenantCustomRolesCount Custom Role definitions ($scopeNamingSummary)
-"@)
- }
- #endregion SUMMARYtenanttotalcustomroles
-
- #region SUMMARYOrphanedCustomRoles
- $startSUMMARYOrphanedCustomRoles = Get-Date
- Write-Host ' processing TenantSummary Custom Roles orphaned'
- if ($getMgParentName -eq 'Tenant Root') {
- $arrayCustomRolesOrphanedFinalIncludingResourceGroups = [System.Collections.ArrayList]@()
-
- if (($tenantCustomRoles).count -gt 0) {
- $mgSubRoleAssignmentsArrayRoleDefinitionIdUnique = $mgSubRoleAssignmentsArrayFromHTValues.RoleDefinitionId | Sort-Object -Unique
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- $rgResRoleAssignmentsArrayRoleDefinitionIdUnique = $rgResRoleAssignmentsArrayFromHTValues.RoleDefinitionId | Sort-Object -Unique
- }
- foreach ($customRoleAll in $tenantCustomRoles) {
- $roleIsUsed = $false
- if (($mgSubRoleAssignmentsArrayRoleDefinitionIdUnique) -contains ($customRoleAll.Id)) {
- $roleIsUsed = $true
- }
-
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- if ($roleIsUsed -eq $false) {
- if (($rgResRoleAssignmentsArrayRoleDefinitionIdUnique) -contains ($customRoleAll.Id)) {
- $roleIsUsed = $true
- }
- }
- }
-
- #role used in a policyDef (rule roledefinitionIds)
- if ($htRoleDefinitionIdsUsedInPolicy.Keys -contains "/providers/Microsoft.Authorization/roleDefinitions/$($customRoleAll.Id)") {
- $roleIsUsed = $true
- }
-
- if ($roleIsUsed -eq $false) {
- $null = $arrayCustomRolesOrphanedFinalIncludingResourceGroups.Add($customRoleAll)
- }
- }
- }
-
- if (($arrayCustomRolesOrphanedFinalIncludingResourceGroups).count -gt 0) {
- $tfCount = ($arrayCustomRolesOrphanedFinalIncludingResourceGroups).count
- $htmlTableId = 'TenantSummary_customRolesOrphaned'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arrayCustomRolesOrphanedFinalIncludingResourceGroups).count) Orphaned Custom Role definitions ($scopeNamingSummary)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Role Name
-RoleId
-Assignable Scopes
-
-
-
-"@)
- $htmlSUMMARYOrphanedCustomRoles = $null
- $htmlSUMMARYOrphanedCustomRoles = foreach ($customRoleOrphaned in $arrayCustomRolesOrphanedFinalIncludingResourceGroups | Sort-Object @{Expression = { $_.Name } }) {
- @"
-
-$($customRoleOrphaned.Name -replace '<', '<' -replace '>', '>')
-$($customRoleOrphaned.Id)
-$(($customRoleOrphaned.AssignableScopes).count) ($($customRoleOrphaned.AssignableScopes -join "$CsvDelimiterOpposite "))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYOrphanedCustomRoles)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arrayCustomRolesOrphanedFinalIncludingResourceGroups).count) Orphaned Custom Role definitions ($scopeNamingSummary)
-"@)
- }
- #not renant root
- }
- else {
- $mgs = (($optimizedTableForPathQueryMg.where( { $_.mgId -ne '' -and $_.Level -ne '0' })) | Select-Object MgId -Unique)
- $arrayCustomRolesOrphanedFinalIncludingResourceGroups = [System.Collections.ArrayList]@()
-
- $mgSubRoleAssignmentsArrayRoleDefinitionIdUnique = $mgSubRoleAssignmentsArrayFromHTValues.RoleDefinitionId | Sort-Object -Unique
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- $rgResRoleAssignmentsArrayRoleDefinitionIdUnique = $rgResRoleAssignmentsArrayFromHTValues.RoleDefinitionId | Sort-Object -Unique
- }
- if (($tenantCustomRoles).count -gt 0) {
- foreach ($customRoleAll in $tenantCustomRoles) {
- $roleIsUsed = $false
- $customRoleAssignableScopes = $customRoleAll.AssignableScopes
- foreach ($customRoleAssignableScope in $customRoleAssignableScopes) {
- if (($customRoleAssignableScope) -like '/providers/Microsoft.Management/managementGroups/*') {
- $roleAssignableScopeMg = $customRoleAssignableScope -replace '/providers/Microsoft.Management/managementGroups/', ''
- if ($mgs.MgId -notcontains ($roleAssignableScopeMg)) {
- #assignableScope outside of the ManagementGroupId Scope
- $roleIsUsed = $true
- Continue
- }
- }
- }
- if ($roleIsUsed -eq $false) {
- if (($mgSubRoleAssignmentsArrayRoleDefinitionIdUnique) -contains ($customRoleAll.Id)) {
- $roleIsUsed = $true
- }
- }
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- if ($roleIsUsed -eq $false) {
- if (($rgResRoleAssignmentsArrayRoleDefinitionIdUnique) -contains ($customRoleAll.Id)) {
- $roleIsUsed = $true
- }
- }
- }
-
- #role used in a policyDef (rule roledefinitionIds)
- if ($htRoleDefinitionIdsUsedInPolicy.Keys -contains "/providers/Microsoft.Authorization/roleDefinitions/$($customRoleAll.Id)") {
- $roleIsUsed = $true
- }
-
- if ($roleIsUsed -eq $false) {
- $null = $arrayCustomRolesOrphanedFinalIncludingResourceGroups.Add($customRoleAll)
- }
- }
- }
-
- if (($arrayCustomRolesOrphanedFinalIncludingResourceGroups).count -gt 0) {
- $tfCount = ($arrayCustomRolesOrphanedFinalIncludingResourceGroups).count
- $htmlTableId = 'TenantSummary_customRolesOrphaned'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arrayCustomRolesOrphanedFinalIncludingResourceGroups).count) Orphaned Custom Role definitions ($scopeNamingSummary)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Role Name
-RoleId
-Role Assignable Scopes
-
-
-
-"@)
- $htmlSUMMARYOrphanedCustomRoles = $null
- $htmlSUMMARYOrphanedCustomRoles = foreach ($inScopeCustomRole in $arrayCustomRolesOrphanedFinalIncludingResourceGroups | Sort-Object @{Expression = { $_.Name } }) {
- @"
-
-$($inScopeCustomRole.Name -replace '<', '<' -replace '>', '>')
-$($inScopeCustomRole.Id)
-$(($inScopeCustomRole.AssignableScopes).count) ($($inScopeCustomRole.AssignableScopes -join "$CsvDelimiterOpposite "))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYOrphanedCustomRoles)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($arrayCustomRolesOrphanedFinalIncludingResourceGroups).count) Orphaned Custom Role definitions ($scopeNamingSummary)
-"@)
- }
- }
- $endSUMMARYOrphanedCustomRoles = Get-Date
- Write-Host " SUMMARYOrphanedCustomRoles duration: $((New-TimeSpan -Start $startSUMMARYOrphanedCustomRoles -End $endSUMMARYOrphanedCustomRoles).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYOrphanedCustomRoles -End $endSUMMARYOrphanedCustomRoles).TotalSeconds) seconds)"
-
- #endregion SUMMARYOrphanedCustomRoles
-
- #region SUMMARYOrphanedRoleAssignments
- Write-Host ' processing TenantSummary RoleAssignments orphaned'
- $roleAssignmentsOrphanedAll = ($rbacBaseQuery.where( { $_.RoleAssignmentIdentityObjectType -eq 'Unknown' })) | Sort-Object -Property RoleAssignmentId
- $roleAssignmentsOrphanedUnique = $roleAssignmentsOrphanedAll | Sort-Object -Property RoleAssignmentId -Unique
-
- if (($roleAssignmentsOrphanedUnique).count -gt 0) {
- $tfCount = ($roleAssignmentsOrphanedUnique).count
- $htmlTableId = 'TenantSummary_roleAssignmentsOrphaned'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($roleAssignmentsOrphanedUnique).count) Orphaned Role assignments ($scopeNamingSummary)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Role AssignmentId
-Role Name
-RoleId
-Impacted Mg/Sub
-
-
-
-"@)
- $htmlSUMMARYOrphanedRoleAssignments = $null
- foreach ($roleAssignmentOrphanedUnique in $roleAssignmentsOrphanedUnique) {
- $hlpRoleAssignmentsAll = $roleAssignmentsOrphanedAll.where( { $_.RoleAssignmentId -eq $roleAssignmentOrphanedUnique.RoleAssignmentId })
- $impactedMgs = $hlpRoleAssignmentsAll.where( { [String]::IsNullOrEmpty($_.SubscriptionId) }) | Sort-Object -Property MgId
- $impactedSubs = $hlpRoleAssignmentsAll.where( { -not [String]::IsNullOrEmpty($_.SubscriptionId) }) | Sort-Object -Property SubscriptionId
- $htmlSUMMARYOrphanedRoleAssignments += @"
-
-$($roleAssignmentOrphanedUnique.RoleAssignmentId)
-$($roleAssignmentOrphanedUnique.RoleDefinitionName -replace '<', '<' -replace '>', '>')
-$($roleAssignmentOrphanedUnique.RoleDefinitionId)
-Mg: $(($impactedMgs).count); Sub: $(($impactedSubs).count)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYOrphanedRoleAssignments)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($roleAssignmentsOrphanedUnique).count) Orphaned Role assignments ($scopeNamingSummary)
-"@)
- }
- #endregion SUMMARYOrphanedRoleAssignments
-
- #region SUMMARYClassicAdministrators
- Write-Host ' processing TenantSummary ClassicAdministrators'
-
- if ($htClassicAdministrators.Keys.Count -gt 0) {
- $tfCount = $htClassicAdministrators.Values.ClassicAdministrators.Count
- $htmlTableId = 'TenantSummary_ClassicAdministrators'
- [void]$htmlTenantSummary.AppendLine(@"
-
$($tfCount) Classic Administrators
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-MgPath
-Role
-Identity
-
-
-
-"@)
- $htmlSUMMARYClassicAdministrators = $null
- $classicAdministrators = $htClassicAdministrators.Values.ClassicAdministrators | Sort-Object -Property Subscription, Role, Identity
- if (-not $NoCsvExport) {
- $csvFilename = "$($filename)_ClassicAdministrators"
- Write-Host " Exporting ClassicAdministrators CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
- $classicAdministrators | Select-Object -ExcludeProperty Id | Sort-Object -Property Subscription, SubscriptionId, Role | Export-Csv -Encoding utf8 -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter $csvDelimiter -NoTypeInformation
- }
- $htmlSUMMARYClassicAdministrators = foreach ($classicAdministrator in $classicAdministrators) {
- @"
-
-$($classicAdministrator.Subscription)
-$($classicAdministrator.SubscriptionId)
-$($classicAdministrator.SubscriptionMgPath)
-$($classicAdministrator.Role)
-$($classicAdministrator.Identity)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYClassicAdministrators)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No ClassicAdministrators
-'@)
- }
- #endregion SUMMARYClassicAdministrators
-
- #region SUMMARYRoleAssignmentsAll
- $startRoleAssignmentsAll = Get-Date
- Write-Host ' processing TenantSummary RoleAssignments'
-
- $startCreateRBACAllHTMLbeforeForeach = Get-Date
-
- if ($azAPICallConf['htParameters'].LargeTenant -or $azAPICallConf['htParameters'].RBACAtScopeOnly) {
- $rbacAllAtScope = ($rbacAll.where( { ((-not [string]::IsNullOrEmpty($_.SubscriptionId) -and $_.scope -notlike 'inherited *')) -or ([string]::IsNullOrEmpty($_.SubscriptionId)) }))
- $rbacAllCount = $rbacAllAtScope.Count
- $rbacAllUniqueCount = ($rbacAllAtScope.where({ $_.roleAssignmentId }).RoleAssignmentId | Sort-Object -Unique).count
- }
- else {
- $rbacAllCount = $rbacAll.Count
- $rbacAllUniqueCount = ($rbacAll.where({ $_.roleAssignmentId }).RoleAssignmentId | Sort-Object -Unique).count
- }
-
- if ($rbacAllCount -gt 0) {
- $uniqueRoleAssignmentsCount = ($rbacAll.RoleAssignmentId | Sort-Object -Unique).count
- $tfCount = $rbacAllCount
-
- if (-not $NoCsvExport) {
- $startCreateRBACAllCSV = Get-Date
-
- $csvFilename = "$($filename)_RoleAssignments"
- Write-Host " Exporting RoleAssignments CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
- if ($CsvExportUseQuotesAsNeeded) {
- if ($azAPICallConf['htParameters'].LargeTenant -or $azAPICallConf['htParameters'].RBACAtScopeOnly) {
- $rbacAllAtScope | Sort-Object -Property Level, RoleAssignmentId, MgId, SubscriptionId, RoleClear, ObjectId | Select-Object -ExcludeProperty Role, RbacRelatedPolicyAssignment | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter "$csvDelimiter" -NoTypeInformation -UseQuotes AsNeeded
- }
- else {
- $rbacAll | Sort-Object -Property Level, RoleAssignmentId, MgId, SubscriptionId, RoleClear, ObjectId | Select-Object -ExcludeProperty Role, RbacRelatedPolicyAssignment | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter "$csvDelimiter" -NoTypeInformation -UseQuotes AsNeeded
- }
- }
- else {
- if ($azAPICallConf['htParameters'].LargeTenant -or $azAPICallConf['htParameters'].RBACAtScopeOnly) {
- $rbacAllAtScope | Sort-Object -Property Level, RoleAssignmentId, MgId, SubscriptionId, RoleClear, ObjectId | Select-Object -ExcludeProperty Role, RbacRelatedPolicyAssignment | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
- else {
- $rbacAll | Sort-Object -Property Level, RoleAssignmentId, MgId, SubscriptionId, RoleClear, ObjectId | Select-Object -ExcludeProperty Role, RbacRelatedPolicyAssignment | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
- }
-
- $endCreateRBACAllCSV = Get-Date
- Write-Host " CreateRBACAll CSV duration: $((New-TimeSpan -Start $startCreateRBACAllCSV -End $endCreateRBACAllCSV).TotalMinutes) minutes ($((New-TimeSpan -Start $startCreateRBACAllCSV -End $endCreateRBACAllCSV).TotalSeconds) seconds)"
- }
-
- if ($tfCount -gt $HtmlTableRowsLimit) {
- Write-Host " !Skipping TenantSummary RoleAssignments HTML processing as $tfCount lines is exceeding the critical rows limit of $HtmlTableRowsLimit" -ForegroundColor Yellow
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($rbacAllCount) Role assignments ($uniqueRoleAssignmentsCount unique)
-
-
-
Output of $tfCount lines would exceed the html rows limit of $HtmlTableRowsLimit (html file potentially would become unresponsive). Work with the CSV file $($csvFilename).csv | Note: the CSV file will only exist if you did NOT use parameter -NoCsvExport
-
You can adjust the html row limit by using parameter -HtmlTableRowsLimit
-
You can reduce the number of lines by using parameter -LargeTenant and/or -DoNotIncludeResourceGroupsAndResourcesOnRBAC
-
Check the parameters documentation Azure Governance Visualizer docs
-
-"@)
- }
- else {
-
- $roleAssignmentsInfo = @()
- #all
- $roleAssignmentsInfo += "All: $($rbacAllUniqueCount)"
- #static
- $roleAssignmentsInfo += "Standing: $((($rbacAll.where({ $_.RoleAssignmentPIMRelated -eq $false })).roleAssignmentId | Sort-Object -Unique).count)"
- #PIM
- foreach ($pimAssignmentInfo in ($rbacAll.where({ $_.RoleAssignmentPIMRelated -and $_.Scope -notlike 'inherited*' })) | Group-Object -Property RoleAssignmentPIMAssignmentType) {
- $roleAssignmentsInfo += "PIM-$($pimAssignmentInfo.Name): $($pimAssignmentInfo.Count)"
- }
-
- $htmlTableId = 'TenantSummary_roleAssignmentsAll'
- $noteOrNot = ''
- [void]$htmlTenantSummary.AppendLine(@"
-
$($rbacAllCount) Role assignment related entries (unique -> $($roleAssignmentsInfo -join ', '))
-
-
-
Download CSV
semicolon |
comma
-
*Depending on the number of rows and your computer´s performance the table may respond with delay, download the csv for better filtering experience
-"@)
-
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-Scope
-Management Group Id
-Management Group Name
-SubscriptionId
-Subscription Name
-Assignment Scope
-Role
-Role Id
-Role Type
-Data
-Can do Role assignment
-Identity Displayname
-Identity SignInName
-Identity ObjectId
-Identity Type
-Applicability
-Applies through membership
-Group Details
-PIM
-PIM assignment type
-PIM start
-PIM end
-Role AssignmentId
-Related Policy Assignment $noteOrNot
-CreatedOn
-CreatedBy
-
-
-
-"@)
- $cnter = 0
- $roleAssignmentsAllCount = $rbacAllCount
- $htmlSummaryRoleAssignmentsAll = $null
- $htmlTenantSummary | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $htmlTenantSummary = [System.Text.StringBuilder]::new()
-
- $endCreateRBACAllHTMLbeforeForeach = Get-Date
- Write-Host " CreateRBACAll HTML before Foreach duration: $((New-TimeSpan -Start $startCreateRBACAllHTMLbeforeForeach -End $endCreateRBACAllHTMLbeforeForeach).TotalMinutes) minutes ($((New-TimeSpan -Start $startCreateRBACAllHTMLbeforeForeach -End $endCreateRBACAllHTMLbeforeForeach).TotalSeconds) seconds)"
-
- $startSortRBACAll = Get-Date
- if ($azAPICallConf['htParameters'].LargeTenant -or $azAPICallConf['htParameters'].RBACAtScopeOnly) {
- $rbacAllSorted = $rbacAllAtScope | Sort-Object -Property Level, MgName, MgId, SubscriptionName, SubscriptionId, Scope, Role, RoleId, ObjectId, RoleAssignmentId
- }
- else {
- $rbacAllSorted = $rbacAll | Sort-Object -Property Level, MgName, MgId, SubscriptionName, SubscriptionId, Scope, Role, RoleId, ObjectId, RoleAssignmentId
- }
-
- $endSortRBACAll = Get-Date
- Write-Host " Sort RBACAll duration: $((New-TimeSpan -Start $startSortRBACAll -End $endSortRBACAll).TotalMinutes) minutes ($((New-TimeSpan -Start $startSortRBACAll -End $endSortRBACAll).TotalSeconds) seconds)"
-
- $startCreateRBACAllHTMLForeach = Get-Date
- $htmlSummaryRoleAssignmentsAll = [System.Text.StringBuilder]::new()
- foreach ($roleAssignment in $rbacAllSorted) {
- $cnter++
- if ($cnter % 1000 -eq 0) {
- Write-Host " create HTML $cnter of $rbacAllCount RoleAssignments processed"
- if ($cnter % 5000 -eq 0) {
- Write-Host ' appending..'
- $htmlSummaryRoleAssignmentsAll | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $htmlSummaryRoleAssignmentsAll = [System.Text.StringBuilder]::new()
- }
- }
-
- if ($roleAssignment.RoleType -eq 'Custom') {
- $roleName = ($roleAssignment.Role -replace '<', '<' -replace '>', '>')
- }
- else {
- $roleName = $roleAssignment.Role
- }
-
- [void]$htmlSummaryRoleAssignmentsAll.AppendFormat(
- @'
-
-{0}
-{1}
-{2}
-{3}
-{4}
-{5}
-{6}
-{7}
-{8}
-{9}
-{10}
-{11}
-{12}
-{13}
-{14}
-{15}
-{16}
-{17}
-{18}
-{19}
-{20}
-{21}
-{22}
-{23}
-{24}
-{25}
-
-'@, $roleAssignment.ScopeTenOrMgOrSubOrRGOrRes,
- $roleAssignment.MgId,
- ($roleAssignment.MgName -replace '<', '<' -replace '>', '>'),
- $roleAssignment.SubscriptionId,
- $roleAssignment.SubscriptionName,
- $roleAssignment.Scope,
- $roleName,
- $roleAssignment.RoleId,
- $roleAssignment.RoleType,
- $roleAssignment.RoleDataRelated,
- $roleAssignment.RoleCanDoRoleAssignments,
- $roleAssignment.ObjectDisplayName,
- $roleAssignment.ObjectSignInName,
- $roleAssignment.ObjectId,
- $roleAssignment.ObjectType,
- $roleAssignment.AssignmentType,
- $roleAssignment.AssignmentInheritFrom,
- $roleAssignment.GroupMembersCount,
- $roleAssignment.RoleAssignmentPIMRelated,
- $roleAssignment.RoleAssignmentPIMAssignmentType,
- $roleAssignment.RoleAssignmentPIMAssignmentSlotStart,
- $roleAssignment.RoleAssignmentPIMAssignmentSlotEnd,
- $roleAssignment.RoleAssignmentId,
- ($roleAssignment.RbacRelatedPolicyAssignment),
- $roleAssignment.CreatedOn,
- $roleAssignment.CreatedBy
- )
-
- }
- $start = Get-Date
- [void]$htmlTenantSummary.AppendLine($htmlSummaryRoleAssignmentsAll)
-
- $htmlTenantSummary | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $htmlSummaryRoleAssignmentsAll = $null #cleanup
- $htmlTenantSummary = [System.Text.StringBuilder]::new()
- $end = Get-Date
-
- $endCreateRBACAllHTMLForeach = Get-Date
- Write-Host " CreateRBACAll HTML Foreach duration: $((New-TimeSpan -Start $startCreateRBACAllHTMLForeach -End $endCreateRBACAllHTMLForeach).TotalMinutes) minutes ($((New-TimeSpan -Start $startCreateRBACAllHTMLForeach -End $endCreateRBACAllHTMLForeach).TotalSeconds) seconds)"
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
-
- }
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($rbacAllCount) Role assignments
-"@)
- }
-
- $endRoleAssignmentsAll = Get-Date
- Write-Host " SummaryRoleAssignmentsAll duration: $((New-TimeSpan -Start $startRoleAssignmentsAll -End $endRoleAssignmentsAll).TotalMinutes) minutes ($((New-TimeSpan -Start $startRoleAssignmentsAll -End $endRoleAssignmentsAll).TotalSeconds) seconds)"
- #endregion SUMMARYRoleAssignmentsAll
-
- #region SUMMARYPIMEligibility
- if (-not $NoPIMEligibility) {
- $startPIMEligibility = Get-Date
- Write-Host ' processing TenantSummary PIMEligibility'
-
- if ($arrayPIMEligible.Count -gt 0) {
- $tfCount = $arrayPIMEligible.Count
- $htmlTableId = 'TenantSummary_PIMEligibility'
- [void]$htmlTenantSummary.AppendLine(@"
-
$($tfCount) direct PIM Eligible assignments
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-ScopeId
-ScopeName
-MgPath
-MgLevel
-Role
-Role Id
-Role type
-Identity ObjectId
-Identity DisplayName
-Identity SignInName
-Identity Type
-Identity Applicability
-Applies through (AAD Grp)
-PIM Eligibility
-PIM Eligibility inhherted (MG)
-PIM start
-PIM end
-PIM Eligibility Id
-
-
-
-"@)
- $htmlSUMMARYPIMEligibility = $null
- $PIMEligibleEnrichedSorted = $PIMEligibleEnriched | Sort-Object -Property Scope, MgLevel, ScopeName, IdentityDisplayName, PIMEligibilityId
- $tfCountCnt = $PIMEligibleEnrichedSorted.Count
- $htmlSUMMARYPIMEligibility = foreach ($PIMEligible in $PIMEligibleEnrichedSorted) {
- @"
-
-$($PIMEligible.Scope)
-$($PIMEligible.ScopeId)
-$($PIMEligible.ScopeName)
-$($PIMEligible.MgPath -join '/')
-$($PIMEligible.MgLevel)
-$($PIMEligible.Role)
-$($PIMEligible.RoleIdGuid)
-$($PIMEligible.RoleType)
-$($PIMEligible.IdentityObjectId)
-$($PIMEligible.IdentityDisplayName)
-$($PIMEligible.IdentitySignInName)
-$($PIMEligible.IdentityType)
-$($PIMEligible.IdentityApplicability)
-$($PIMEligible.AppliesThrough)
-$($PIMEligible.PIMEligibility)
-$($PIMEligible.PIMEligibilityInheritedFrom)
-$($PIMEligible.PIMEligibilityStartDateTime)
-$($PIMEligible.PIMEligibilityEndDateTime)
-$($PIMEligible.PIMEligibilityId)
-
-"@
- }
-
- if (-not $NoCsvExport) {
- $csvFilename = "$($filename)_PIMEligibility"
- Write-Host " Exporting PIMEligibility CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
- $PIMEligibleEnrichedSorted | Select-Object -ExcludeProperty RoleClear | Export-Csv -Encoding utf8 -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter $csvDelimiter -NoTypeInformation
- }
-
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYPIMEligibility)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No PIM Eligibility
-'@)
- }
-
- $endPIMEligibility = Get-Date
- Write-Host " TenantSummary PIMEligibility duration: $((New-TimeSpan -Start $startPIMEligibility -End $endPIMEligibility).TotalMinutes) minutes ($((New-TimeSpan -Start $startPIMEligibility -End $endPIMEligibility).TotalSeconds) seconds)"
- }
- else {
- if ($azAPICallConf['htParameters'].accountType -ne 'User' -and $NoPIMEligibility) {
- [void]$htmlTenantSummary.AppendLine(@"
-
No PIM Eligibility - parameter -NoPIMEligibility = $NoPIMEligibility
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No PIM Eligibility - run Azure Governance Visualizer with a Service Principal to get PIM Eligibility insights
-'@)
- }
- }
- #endregion SUMMARYPIMEligibility
-
- #region SUMMARYSecurityCustomRoles
- Write-Host ' processing TenantSummary Custom Roles security (owner permissions)'
- $customRolesOwnerAll = ($rbacBaseQuery.where( { $_.RoleSecurityCustomRoleOwner -eq 1 })) | Sort-Object -Property RoleDefinitionId
- $customRolesOwnerHtAll = $tenantCustomRoles.where( { $_.Actions -eq '*' -and ($_.NotActions).length -eq 0 })
- if (($customRolesOwnerHtAll).count -gt 0) {
- $tfCount = ($customRolesOwnerHtAll).count
- $htmlTableId = 'TenantSummary_CustomRoleOwner'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($customRolesOwnerHtAll).count) Custom Role definitions Owner permissions ($scopeNamingSummary)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Role Name
-RoleId
-Role assignments
-Assignable Scopes
-
-
-
-"@)
- $htmlSUMMARYSecurityCustomRoles = $null
- foreach ($customRole in ($customRolesOwnerHtAll | Sort-Object -Property Name, Id)) {
- $customRoleOwnersAllAssignmentsCount = ((($customRolesOwnerAll.where( { $_.RoleDefinitionId -eq $customRole.Id })).RoleAssignmentId | Sort-Object -Unique)).count
- if ($customRoleOwnersAllAssignmentsCount -gt 0) {
- $customRoleRoleAssignmentsArray = [System.Collections.ArrayList]@()
- $customRoleRoleAssignmentIds = ($customRolesOwnerAll.where( { $_.RoleDefinitionId -eq $customRole.Id })).RoleAssignmentId | Sort-Object -Unique
- foreach ($customRoleRoleAssignmentId in $customRoleRoleAssignmentIds) {
- $null = $customRoleRoleAssignmentsArray.Add($customRoleRoleAssignmentId)
- }
- $customRoleRoleAssignmentsOutput = "$customRoleOwnersAllAssignmentsCount ($($customRoleRoleAssignmentsArray -join "$CsvDelimiterOpposite "))"
- }
- else {
- $customRoleRoleAssignmentsOutput = "$customRoleOwnersAllAssignmentsCount"
- }
- $htmlSUMMARYSecurityCustomRoles += @"
-
-$($customRole.Name -replace '<', '<' -replace '>', '>')
-$($customRole.Id)
-$($customRoleRoleAssignmentsOutput)
-$(($customRole.AssignableScopes).count) ($($customRole.AssignableScopes -join "$CsvDelimiterOpposite "))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSecurityCustomRoles)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($customRolesOwnerHtAll).count) Custom Role definitions Owner permissions ($scopeNamingSummary)
-"@)
- }
- #endregion SUMMARYSecurityCustomRoles
-
- #region SUMMARYSecurityRolesCanDoRoleAssignments
- Write-Host ' processing TenantSummary Roles security (can apply Role assignments)'
- if ($tenantAllRolesCanDoRoleAssignmentsCount -gt 0) {
-
- #$roleAssignments4RolesCanDoRoleAssignments = (($rbacBaseQuery.where( { $_.RoleCanDoRoleAssignments -eq $true })) | Sort-Object -Property RoleAssignmentId -Unique) | Select-Object RoleAssignmentId, RoleDefinitionId
- $roleAssignments4RolesCanDoRoleAssignments = (($rbacBaseQuery | Sort-Object -Property RoleAssignmentId -Unique).where( { $_.RoleCanDoRoleAssignments -eq $true })) | Select-Object RoleAssignmentId, RoleDefinitionId
- $htRoleAssignments4RolesCanDoRoleAssignments = @{}
- foreach ($roleAssignment4RolesCanDoRoleAssignments in $roleAssignments4RolesCanDoRoleAssignments) {
- if (-not $htRoleAssignments4RolesCanDoRoleAssignments.($roleAssignment4RolesCanDoRoleAssignments.RoleDefinitionId)) {
- $htRoleAssignments4RolesCanDoRoleAssignments.($roleAssignment4RolesCanDoRoleAssignments.RoleDefinitionId) = @{}
- $htRoleAssignments4RolesCanDoRoleAssignments.($roleAssignment4RolesCanDoRoleAssignments.RoleDefinitionId).roleAssignments = [System.Collections.ArrayList]@()
- }
- $null = $htRoleAssignments4RolesCanDoRoleAssignments.($roleAssignment4RolesCanDoRoleAssignments.RoleDefinitionId).roleAssignments.Add($roleAssignment4RolesCanDoRoleAssignments.RoleAssignmentId)
- }
-
- $tfCount = $tenantAllRolesCanDoRoleAssignmentsCount
- $htmlTableId = 'TenantSummary_RolesCanDoRoleAssignments'
- [void]$htmlTenantSummary.AppendLine(@"
-
$($tenantAllRolesCanDoRoleAssignmentsCount) Role definitions can apply Role assignments
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Role Name
-RoleId
-Type
-Role assignments
-Assignable Scopes
-
-
-
-"@)
- $htmlSUMMARYSecurityRolesCanDoRoleAssignments = $null
- foreach ($role in ($tenantAllRolesCanDoRoleAssignments | Sort-Object -Property Name)) {
- if ($role.IsCustom) {
- $roleType = 'Custom'
- $roleAssignableScopes = "$(($role.AssignableScopes).count) ($($role.AssignableScopes -join "$CsvDelimiterOpposite "))"
- }
- else {
- $roleType = 'BuiltIn'
- $roleAssignableScopes = ''
- }
-
- if ($htRoleAssignments4RolesCanDoRoleAssignments.($role.Id).roleAssignments.Count -gt 0) {
- $roleAssignments = "$($htRoleAssignments4RolesCanDoRoleAssignments.($role.Id).roleAssignments.Count) ($($htRoleAssignments4RolesCanDoRoleAssignments.($role.Id).roleAssignments -join ', '))"
- }
- else {
- $roleAssignments = 0
- }
-
- $htmlSUMMARYSecurityRolesCanDoRoleAssignments += @"
-
-$($role.Name -replace '<', '<' -replace '>', '>')
-$($role.Id)
-$($roleType)
-$($roleAssignments)
-$($roleAssignableScopes)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSecurityRolesCanDoRoleAssignments)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($tenantAllRolesCanDoRoleAssignmentsCount) Role definitions can apply Role assignments
-"@)
- }
- #endregion SUMMARYSecurityRolesCanDoRoleAssignments
-
- #region SUMMARYSecurityOwnerAssignmentSP
- $startSUMMARYSecurityOwnerAssignmentSP = Get-Date
- Write-Host ' processing TenantSummary RoleAssignments security (owner SP)'
- $roleAssignmentsOwnerAssignmentSPAll = ($rbacBaseQuery.where( { $_.RoleSecurityOwnerAssignmentSP -eq 1 })) | Sort-Object -Property RoleAssignmentId
- $roleAssignmentsOwnerAssignmentSP = $roleAssignmentsOwnerAssignmentSPAll | Sort-Object -Property RoleAssignmentId -Unique
- if (($roleAssignmentsOwnerAssignmentSP).count -gt 0) {
- $tfCount = ($roleAssignmentsOwnerAssignmentSP).count
- $htmlTableId = 'TenantSummary_roleAssignmentsOwnerAssignmentSP'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($roleAssignmentsOwnerAssignmentSP).count) Owner permission assignments to ServicePrincipal ($scopeNamingSummary)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Role Name
-RoleId
-Role Assignment
-ServicePrincipal (ObjId)
-Impacted Mg/Sub
-
-
-
-"@)
- $htmlSUMMARYSecurityOwnerAssignmentSP = $null
- $htmlSUMMARYSecurityOwnerAssignmentSP = foreach ($roleAssignmentOwnerAssignmentSP in ($roleAssignmentsOwnerAssignmentSP)) {
- $hlpRoleAssignmentsAll = $roleAssignmentsOwnerAssignmentSPAll.where( { $_.RoleAssignmentId -eq $roleAssignmentOwnerAssignmentSP.RoleAssignmentId })
- $impactedMgs = $hlpRoleAssignmentsAll.where( { [String]::IsNullOrEmpty($_.SubscriptionId) })
- $impactedSubs = $hlpRoleAssignmentsAll.where( { -not [String]::IsNullOrEmpty($_.SubscriptionId) })
- $servicePrincipal = $roleAssignmentsOwnerAssignmentSP.where( { $_.RoleAssignmentId -eq $roleAssignmentOwnerAssignmentSP.RoleAssignmentId }) | Get-Unique
- @"
-
-$($roleAssignmentOwnerAssignmentSP.RoleDefinitionName -replace '<', '<' -replace '>', '>')
-$($roleAssignmentOwnerAssignmentSP.RoleDefinitionId)
-$($roleAssignmentOwnerAssignmentSP.RoleAssignmentId)
-$($servicePrincipal.RoleAssignmentIdentityDisplayname) ($($servicePrincipal.RoleAssignmentIdentityObjectId))
-Mg: $(($impactedMgs.mgid | Sort-Object -Unique).count); Sub: $(($impactedSubs.subscriptionId | Sort-Object -Unique).count)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSecurityOwnerAssignmentSP)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($roleAssignmentsOwnerAssignmentSP).count) Owner permission assignments to ServicePrincipal ($scopeNamingSummary)
-"@)
- }
- $endSUMMARYSecurityOwnerAssignmentSP = Get-Date
- Write-Host " TenantSummary RoleAssignments security (owner SP) duration: $((New-TimeSpan -Start $startSUMMARYSecurityOwnerAssignmentSP -End $endSUMMARYSecurityOwnerAssignmentSP).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYSecurityOwnerAssignmentSP -End $endSUMMARYSecurityOwnerAssignmentSP).TotalSeconds) seconds)"
- #endregion SUMMARYSecurityOwnerAssignmentSP
-
- #region SUMMARYSecurityOwnerAssignmentNotGroup
- Write-Host ' processing TenantSummary RoleAssignments security (owner notGroup)'
- $startSUMMARYSecurityOwnerAssignmentNotGroup = Get-Date
-
- $roleAssignmentsOwnerAssignmentNotGroup = $rbacBaseQueryArrayListNotGroupOwner | Sort-Object -Property RoleAssignmentId -Unique
- $roleAssignmentsOwnerAssignmentNotGroupGrouped = ($rbacBaseQueryArrayListNotGroupOwner | Group-Object -Property roleassignmentId)
-
- if (($roleAssignmentsOwnerAssignmentNotGroup).count -gt 0) {
- $tfCount = ($roleAssignmentsOwnerAssignmentNotGroup).count
- $htmlTableId = 'TenantSummary_roleAssignmentsOwnerAssignmentNotGroup'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($roleAssignmentsOwnerAssignmentNotGroup).count) Owner permission assignments to notGroup ($scopeNamingSummary)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Role Name
-RoleId
-Role Assignment
-Obj Type
-Obj DisplayName
-Obj SignInName
-ObjId
-Impacted Mg/Sub
-
-
-
-"@)
- $htmlSUMMARYSecurityOwnerAssignmentNotGroup = $null
- $htmlSUMMARYSecurityOwnerAssignmentNotGroup = foreach ($roleAssignmentOwnerAssignmentNotGroup in ($roleAssignmentsOwnerAssignmentNotGroup)) {
- $impactedMgSubBaseQuery = $roleAssignmentsOwnerAssignmentNotGroupGrouped.where( { $_.Name -eq $roleAssignmentOwnerAssignmentNotGroup.RoleAssignmentId })
- $impactedMgs = $impactedMgSubBaseQuery.Group.where( { [String]::IsNullOrEmpty($_.SubscriptionId) })
- $impactedSubs = $impactedMgSubBaseQuery.Group.where( { -not [String]::IsNullOrEmpty($_.SubscriptionId) })
- @"
-
-$($roleAssignmentOwnerAssignmentNotGroup.RoleDefinitionName -replace '<', '<' -replace '>', '>')
-$($roleAssignmentOwnerAssignmentNotGroup.RoleDefinitionId)
-$($roleAssignmentOwnerAssignmentNotGroup.RoleAssignmentId)
-$($roleAssignmentOwnerAssignmentNotGroup.RoleAssignmentIdentityObjectType)
-$($roleAssignmentOwnerAssignmentNotGroup.RoleAssignmentIdentityDisplayname)
-$($roleAssignmentOwnerAssignmentNotGroup.RoleAssignmentIdentitySignInName)
-$($roleAssignmentOwnerAssignmentNotGroup.RoleAssignmentIdentityObjectId)
-Mg: $(($impactedMgs.mgid | Sort-Object -Unique).count); Sub: $(($impactedSubs.subscriptionId | Sort-Object -Unique).count)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSecurityOwnerAssignmentNotGroup)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($roleAssignmentsOwnerAssignmentNotGroup).count) Owner permission assignments to notGroup ($scopeNamingSummary)
-"@)
- }
- $endSUMMARYSecurityOwnerAssignmentNotGroup = Get-Date
- Write-Host " TenantSummary RoleAssignments security (owner notGroup) duration: $((New-TimeSpan -Start $startSUMMARYSecurityOwnerAssignmentNotGroup -End $endSUMMARYSecurityOwnerAssignmentNotGroup).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYSecurityOwnerAssignmentNotGroup -End $endSUMMARYSecurityOwnerAssignmentNotGroup).TotalSeconds) seconds)"
- #endregion SUMMARYSecurityOwnerAssignmentNotGroup
-
- #region SUMMARYSecurityUserAccessAdministratorAssignmentNotGroup
- $startSUMMARYSecurityUserAccessAdministratorAssignmentNotGroup = Get-Date
- Write-Host ' processing TenantSummary RoleAssignments security (userAccessAdministrator notGroup)'
- $roleAssignmentsUserAccessAdministratorAssignmentNotGroup = $rbacBaseQueryArrayListNotGroupUserAccessAdministrator | Sort-Object -Property RoleAssignmentId -Unique
- $roleAssignmentsUserAccessAdministratorAssignmentNotGroupGrouped = ($rbacBaseQueryArrayListNotGroupUserAccessAdministrator | Group-Object -Property roleassignmentId)
-
- if (($roleAssignmentsUserAccessAdministratorAssignmentNotGroup).count -gt 0) {
- $tfCount = ($roleAssignmentsUserAccessAdministratorAssignmentNotGroup).count
- $htmlTableId = 'TenantSummary_roleAssignmentsUserAccessAdministratorAssignmentNotGroup'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($roleAssignmentsUserAccessAdministratorAssignmentNotGroup).count) UserAccessAdministrator permission assignments to notGroup ($scopeNamingSummary)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Role Name
-RoleId
-Role Assignment
-Obj Type
-Obj DisplayName
-Obj SignInName
-ObjId
-Impacted Mg/Sub
-
-
-
-"@)
- $htmlSUMMARYSecurityUserAccessAdministratorAssignmentNotGroup = $null
- $htmlSUMMARYSecurityUserAccessAdministratorAssignmentNotGroup = foreach ($roleAssignmentUserAccessAdministratorAssignmentNotGroup in ($roleAssignmentsUserAccessAdministratorAssignmentNotGroup)) {
- $impactedMgSubBaseQuery = $roleAssignmentsUserAccessAdministratorAssignmentNotGroupGrouped.where( { $_.Name -eq $roleAssignmentUserAccessAdministratorAssignmentNotGroup.RoleAssignmentId })
- $impactedMgs = $impactedMgSubBaseQuery.Group.where( { [String]::IsNullOrEmpty($_.SubscriptionId) })
- $impactedSubs = $impactedMgSubBaseQuery.Group.where( { -not [String]::IsNullOrEmpty($_.SubscriptionId) })
- @"
-
-$($roleAssignmentUserAccessAdministratorAssignmentNotGroup.RoleDefinitionName -replace '<', '<' -replace '>', '>')
-$($roleAssignmentUserAccessAdministratorAssignmentNotGroup.RoleDefinitionId)
-$($roleAssignmentUserAccessAdministratorAssignmentNotGroup.RoleAssignmentId)
-$($roleAssignmentUserAccessAdministratorAssignmentNotGroup.RoleAssignmentIdentityObjectType)
-$($roleAssignmentUserAccessAdministratorAssignmentNotGroup.RoleAssignmentIdentityDisplayname)
-$($roleAssignmentUserAccessAdministratorAssignmentNotGroup.RoleAssignmentIdentitySignInName)
-$($roleAssignmentUserAccessAdministratorAssignmentNotGroup.RoleAssignmentIdentityObjectId)
-Mg: $(($impactedMgs.mgid | Sort-Object -Unique).count); Sub: $(($impactedSubs.subscriptionId | Sort-Object -Unique).count)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSecurityUserAccessAdministratorAssignmentNotGroup)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($roleAssignmentsUserAccessAdministratorAssignmentNotGroup).count) UserAccessAdministrator permission assignments to notGroup ($scopeNamingSummary)
-"@)
- }
- $endSUMMARYSecurityUserAccessAdministratorAssignmentNotGroup = Get-Date
- Write-Host " TenantSummary RoleAssignments security (userAccessAdministrator notGroup) duration: $((New-TimeSpan -Start $startSUMMARYSecurityUserAccessAdministratorAssignmentNotGroup -End $endSUMMARYSecurityUserAccessAdministratorAssignmentNotGroup).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYSecurityUserAccessAdministratorAssignmentNotGroup -End $endSUMMARYSecurityUserAccessAdministratorAssignmentNotGroup).TotalSeconds) seconds)"
- #endregion SUMMARYSecurityUserAccessAdministratorAssignmentNotGroup
-
- #region SUMMARYSecurityGuestUserHighPriviledgesAssignments
-
- $startSUMMARYSecurityGuestUserHighPriviledgesAssignments = Get-Date
- Write-Host ' processing TenantSummary RoleAssignments security (high privileged Guest User)'
- $highPrivilegedGuestUserRoleAssignments = $rbacAll.where( { ($_.RoleId -eq '8e3af657-a8ff-443c-a75c-2fe8c4bcb635' -or $_.RoleId -eq '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') -and $_.ObjectType -eq 'User Guest' }) | Sort-Object -Property RoleAssignmentId, ObjectId -Unique
- $highPrivilegedGuestUserRoleAssignmentsCount = ($highPrivilegedGuestUserRoleAssignments).Count
- if ($highPrivilegedGuestUserRoleAssignmentsCount -gt 0) {
- $tfCount = $highPrivilegedGuestUserRoleAssignmentsCount
- $htmlTableId = 'TenantSummary_SecurityGuestUserHighPriviledgesAssignments'
- [void]$htmlTenantSummary.AppendLine(@"
-
$($highPrivilegedGuestUserRoleAssignmentsCount) Guest Users with high permissions ($scopeNamingSummary)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Role Name
-RoleId
-Role Assignment
-Obj Type
-Obj DisplayName
-Obj SignInName
-ObjId
-Assignment direct/indirect
-
-
-
-"@)
- $htmlSUMMARYSecurityGuestUserHighPriviledgesAssignments = $null
- $htmlSUMMARYSecurityGuestUserHighPriviledgesAssignments = foreach ($highPrivilegedGuestUserRoleAssignment in ($highPrivilegedGuestUserRoleAssignments)) {
- if ($highPrivilededGuestUserRoleAssignment.AssignmentType -eq 'indirect') {
- $assignmentInfo = "indirect / AAD Group Membership '$($highPrivilededGuestUserRoleAssignment.AssignmentInheritFrom)'"
- }
- else {
- $assignmentInfo = 'direct'
- }
- @"
-
-$($highPrivilegedGuestUserRoleAssignment.Role <#-replace "<", "<" -replace ">", ">"#>)
-$($highPrivilegedGuestUserRoleAssignment.RoleId)
-$($highPrivilegedGuestUserRoleAssignment.RoleAssignmentId)
-$($highPrivilegedGuestUserRoleAssignment.ObjectType)
-$($highPrivilegedGuestUserRoleAssignment.ObjectDisplayName)
-$($highPrivilegedGuestUserRoleAssignment.ObjectSignInName)
-$($highPrivilegedGuestUserRoleAssignment.ObjectId)
-$assignmentInfo
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSecurityGuestUserHighPriviledgesAssignments)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($highPrivilegedGuestUserRoleAssignmentsCount) Guest Users with high permissions ($scopeNamingSummary)
-"@)
- }
- $endSUMMARYSecurityGuestUserHighPriviledgesAssignments = Get-Date
- Write-Host " TenantSummary RoleAssignments security (high privileged Guest User) duration: $((New-TimeSpan -Start $startSUMMARYSecurityGuestUserHighPriviledgesAssignments -End $endSUMMARYSecurityGuestUserHighPriviledgesAssignments).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYSecurityGuestUserHighPriviledgesAssignments -End $endSUMMARYSecurityGuestUserHighPriviledgesAssignments).TotalSeconds) seconds)"
- #endregion SUMMARYSecurityGuestUserHighPriviledgesAssignments
-
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- #endregion tenantSummaryRBAC
-
- showMemoryUsage
-
- #region tenantSummaryBlueprints
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-'@)
-
- #region SUMMARYBlueprintDefinitions
- Write-Host ' processing TenantSummary Blueprints'
- $blueprintDefinitions = ($blueprintBaseQuery | Where-Object { [String]::IsNullOrEmpty($_.BlueprintAssignmentId) })
- $blueprintDefinitionsCount = ($blueprintDefinitions).count
- if ($blueprintDefinitionsCount -gt 0) {
- $htmlTableId = 'TenantSummary_BlueprintDefinitions'
- [void]$htmlTenantSummary.AppendLine(@"
-
$blueprintDefinitionsCount Blueprint definitions
-
-
Download CSV
semicolon |
comma
-
-
-
-Blueprint Name
-Blueprint DisplayName
-Blueprint Description
-BlueprintId
-
-
-
-"@)
- $htmlSUMMARYBlueprintDefinitions = $null
- $htmlSUMMARYBlueprintDefinitions = foreach ($blueprintDefinition in $blueprintDefinitions | Sort-Object -Property BlueprintName, BlueprintDisplayName) {
- @"
-
-$($blueprintDefinition.BlueprintName -replace '<', '<' -replace '>', '>')
-$($blueprintDefinition.BlueprintDisplayName -replace '<', '<' -replace '>', '>')
-$($blueprintDefinition.BlueprintDescription -replace '<', '<' -replace '>', '>')
-$($blueprintDefinition.BlueprintId -replace '<', '<' -replace '>', '>')
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYBlueprintDefinitions)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$blueprintDefinitionsCount Blueprint definitions
-"@)
- }
- #endregion SUMMARYBlueprintDefinitions
-
- #region SUMMARYBlueprintAssignments
- Write-Host ' processing TenantSummary BlueprintAssignments'
- $blueprintAssignments = ($blueprintBaseQuery | Where-Object { -not [String]::IsNullOrEmpty($_.BlueprintAssignmentId) })
- $blueprintAssignmentsCount = ($blueprintAssignments).count
-
- if ($blueprintAssignmentsCount -gt 0) {
- $htmlTableId = 'TenantSummary_BlueprintAssignments'
- [void]$htmlTenantSummary.AppendLine(@"
-
$blueprintAssignmentsCount Blueprint assignments
-
-
Download CSV
semicolon |
comma
-
-
-
-Blueprint Name
-Blueprint DisplayName
-Blueprint Description
-BlueprintId
-Blueprint Version
-Blueprint AssignmentId
-
-
-
-"@)
- $htmlSUMMARYBlueprintAssignments = $null
- $htmlSUMMARYBlueprintAssignments = foreach ($blueprintAssignment in $blueprintAssignments | Sort-Object -Property level, BlueprintAssignmentId) {
- @"
-
-$($blueprintAssignment.BlueprintName -replace '<', '<' -replace '>', '>')
-$($blueprintAssignment.BlueprintDisplayName -replace '<', '<' -replace '>', '>')
-$($blueprintAssignment.BlueprintDescription -replace '<', '<' -replace '>', '>')
-$($blueprintAssignment.BlueprintId -replace '<', '<' -replace '>', '>')
-$($blueprintAssignment.BlueprintAssignmentVersion)
-$($blueprintAssignment.BlueprintAssignmentId -replace '<', '<' -replace '>', '>')
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYBlueprintAssignments)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$blueprintAssignmentsCount Blueprint assignments
-"@)
- }
- #endregion SUMMARYBlueprintAssignments
-
- #region SUMMARYBlueprintsOrphaned
- Write-Host ' processing TenantSummary Blueprint definitions orphaned'
- $blueprintDefinitionsOrphanedArray = @()
- if ($blueprintDefinitionsCount -gt 0) {
- if ($blueprintAssignmentsCount -gt 0) {
- $blueprintDefinitionsOrphanedArray += foreach ($blueprintDefinition in $blueprintDefinitions) {
- if (($blueprintAssignments.BlueprintId) -notcontains ($blueprintDefinition.BlueprintId)) {
- $blueprintDefinition
- }
- }
- }
- else {
- $blueprintDefinitionsOrphanedArray += foreach ($blueprintDefinition in $blueprintDefinitions) {
- $blueprintDefinition
- }
- }
- }
- $blueprintDefinitionsOrphanedCount = ($blueprintDefinitionsOrphanedArray).count
-
- if ($blueprintDefinitionsOrphanedCount -gt 0) {
-
- $htmlTableId = 'TenantSummary_BlueprintsOrphaned'
- [void]$htmlTenantSummary.AppendLine(@"
-
$blueprintDefinitionsOrphanedCount Orphaned Blueprints
-
-
Download CSV
semicolon |
comma
-
-
-
-Blueprint Name
-Blueprint DisplayName
-Blueprint Description
-BlueprintId
-
-
-
-"@)
- $htmlSUMMARYBlueprintsOrphaned = $null
- $htmlSUMMARYBlueprintsOrphaned = foreach ($blueprintDefinition in $blueprintDefinitionsOrphanedArray | Sort-Object -Property BlueprintId) {
- @"
-
-$($blueprintDefinition.BlueprintName -replace '<', '<' -replace '>', '>')
-$($blueprintDefinition.BlueprintDisplayName -replace '<', '<' -replace '>', '>')
-$($blueprintDefinition.BlueprintDescription -replace '<', '<' -replace '>', '>')
-$($blueprintDefinition.BlueprintId -replace '<', '<' -replace '>', '>')
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYBlueprintsOrphaned)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$blueprintDefinitionsOrphanedCount Orphaned Blueprint definitions
-"@)
- }
- #endregion SUMMARYBlueprintsOrphaned
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- #endregion tenantSummaryBlueprints
-
- showMemoryUsage
-
- #region tenantSummaryManagementGroups
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-'@)
-
- #region SUMMARYMGs
- $startSUMMARYMGs = Get-Date
- Write-Host ' processing TenantSummary ManagementGroups'
-
- $summaryManagementGroups = $optimizedTableForPathQueryMg | Sort-Object -Property Level, mgid, mgParentId
- $summaryManagementGroupsCount = ($summaryManagementGroups).Count
- if ($summaryManagementGroupsCount -gt 0) {
- $tfCount = $summaryManagementGroupsCount
- $htmlTableId = 'TenantSummary_ManagementGroups'
- [void]$htmlTenantSummary.AppendLine(@"
-
$($summaryManagementGroupsCount) Management Groups
-
-
Download CSV
semicolon |
comma
-
-
-
-Level
-ManagementGroup
-ManagementGroup Id
-Mg children (total)
-Mg children (direct)
-Sub children (total)
-Sub children (direct)
-"@)
- if ($azAPICallConf['htParameters'].NoMDfCSecureScore -eq $false) {
- [void]$htmlTenantSummary.AppendLine(@'
-MG MDfC Score
-'@)
- }
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- [void]$htmlTenantSummary.AppendLine(@"
-Cost ($($AzureConsumptionPeriod)d)
-"@)
- }
- [void]$htmlTenantSummary.AppendLine(@'
-Path
-
-
-
-'@)
- $htmlSUMMARYManagementGroups = $null
- $cnter = 0
- $htmlSUMMARYManagementGroups = foreach ($summaryManagementGroup in $summaryManagementGroups) {
-
- $mgPath = $htManagementGroupsMgPath.($summaryManagementGroup.mgId).pathDelimited
-
- if ($summaryManagementGroup.mgid -eq $mgSubPathTopMg -and ($azAPICallConf['checkContext']).Tenant.Id -ne $ManagementGroupId) {
- $pathhlper = "$($mgPath)"
- $arrayTotalCostSummaryMgSummary = 'n/a'
- $mgAllChildMgsCountTotal = 'n/a'
- $mgAllChildMgsCountDirect = 'n/a'
- $mgAllChildSubscriptionsCountTotal = 'n/a'
- $mgAllChildSubscriptionsCountDirect = 'n/a'
- $mgSecureScore = 'n/a'
- }
- else {
-
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- if ($allConsumptionDataCount -gt 0) {
- $arrayTotalCostSummaryMgSummary = @()
- if ($htManagementGroupsCost.($summaryManagementGroup.mgid)) {
- foreach ($currency in $htManagementGroupsCost.($summaryManagementGroup.mgid).currencies) {
- $hlper = $htManagementGroupsCost.($summaryManagementGroup.mgid)
- $totalCost = $hlper."mgTotalCost_$($currency)"
- if ([math]::Round($totalCost, 2) -eq 0) {
- $totalCost = $totalCost.ToString('0.0000')
- }
- else {
- $totalCost = [math]::Round($totalCost, 2).ToString('0.00')
- }
- $totalCostGeneratedByResourceTypes = ($hlper."resourceTypesThatGeneratedCost_$($currency)").Count
- $totalCostGeneratedByResources = $hlper."resourcesThatGeneratedCost_$($currency)"
- $totalCostGeneratedBySubscriptions = $hlper."subscriptionsThatGeneratedCost_$($currency)"
- $arrayTotalCostSummaryMgSummary += "$($totalCost) $($currency) generated by $($totalCostGeneratedByResources) Resources ($($totalCostGeneratedByResourceTypes) ResourceTypes) in $($totalCostGeneratedBySubscriptions) Subscriptions"
- }
- }
- else {
- $arrayTotalCostSummaryMgSummary = 'no consumption data available'
- }
- }
- else {
- $arrayTotalCostSummaryMgSummary = 'no consumption data available'
- }
- }
- $pathhlper = " $($mgPath)"
-
- #childrenMgInfo
- $mgAllChildMgs = [System.Collections.ArrayList]@()
- foreach ($entry in $htManagementGroupsMgPath.keys) {
- if (($htManagementGroupsMgPath.($entry).path) -contains $($summaryManagementGroup.mgid)) {
- $null = $mgAllChildMgs.Add($entry)
- }
- }
- $mgAllChildMgsCountTotal = (($mgAllChildMgs).Count - 1)
- $mgAllChildMgsCountDirect = $htMgDetails.($summaryManagementGroup.mgid).mgChildrenCount
-
- $mgAllChildSubscriptions = [System.Collections.ArrayList]@()
- $mgDirectChildSubscriptions = [System.Collections.ArrayList]@()
- foreach ($entry in $htSubscriptionsMgPath.keys) {
- if (($htSubscriptionsMgPath.($entry).path) -contains $($summaryManagementGroup.mgid)) {
- $null = $mgAllChildSubscriptions.Add($entry)
- }
- if (($htSubscriptionsMgPath.($entry).parent) -eq $($summaryManagementGroup.mgid)) {
- $null = $mgDirectChildSubscriptions.Add($entry)
- }
- }
-
- $mgAllChildSubscriptionsCountTotal = (($mgAllChildSubscriptions).Count)
- $mgAllChildSubscriptionsCountDirect = (($mgDirectChildSubscriptions).Count)
-
- if ($htMgASCSecureScore.($summaryManagementGroup.mgId).SecureScore) {
- if ([string]::IsNullOrEmpty($htMgASCSecureScore.($summaryManagementGroup.mgId).SecureScore) -or [string]::IsNullOrWhiteSpace($htMgASCSecureScore.($summaryManagementGroup.mgId).SecureScore)) {
- $mgSecureScore = 'n/a'
- }
- else {
- $mgSecureScore = $htMgASCSecureScore.($summaryManagementGroup.mgId).SecureScore
- }
- }
- else {
- $mgSecureScore = 'n/a'
- }
- }
-
- @"
-
-$($summaryManagementGroup.level)
-$($summaryManagementGroup.mgName -replace '<', '<' -replace '>', '>')
-$($summaryManagementGroup.mgId)
-$($mgAllChildMgsCountTotal)
-$($mgAllChildMgsCountDirect)
-$($mgAllChildSubscriptionsCountTotal)
-$($mgAllChildSubscriptionsCountDirect)
-"@
- if ($azAPICallConf['htParameters'].NoMDfCSecureScore -eq $false) {
- @"
-$($mgSecureScore)
-"@
- }
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- @"
-$($arrayTotalCostSummaryMgSummary -join ', ')
-"@
- }
- @"
-$($pathhlper)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYManagementGroups)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($summaryManagementGroupsCount) Management Groups
-"@)
- }
- $endSUMMARYMGs = Get-Date
- Write-Host " SUMMARYMGs duration: $((New-TimeSpan -Start $startSUMMARYMGs -End $endSUMMARYMGs).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYMGs -End $endSUMMARYMGs).TotalSeconds) seconds)"
- #endregion SUMMARYMGs
-
- #region SUMMARYMGdefault
- Write-Host ' processing TenantSummary ManagementGroups - default Management Group'
- [void]$htmlTenantSummary.AppendLine(@"
-
Hierarchy Settings | Default Management Group Id: '$($defaultManagementGroupId) ' docs
-"@)
- #endregion SUMMARYMGdefault
-
- #region SUMMARYMGRequireAuthorizationForGroupCreation
- Write-Host ' processing TenantSummary ManagementGroups - requireAuthorizationForGroupCreation Management Group'
- [void]$htmlTenantSummary.AppendLine(@"
-
Hierarchy Settings | Require authorization for Management Group creation: '$($requireAuthorizationForGroupCreation) ' docs
-"@)
- #endregion SUMMARYMGRequireAuthorizationForGroupCreation
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- #endregion tenantSummaryManagementGroups
-
- showMemoryUsage
-
- #region tenantSummarySubscriptionsResourceDefenderPSRule
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-'@)
-
- #region SUMMARYSubs
- $startSUMMARYSubs = Get-Date
- Write-Host ' processing TenantSummary Subscriptions'
- $summarySubscriptions = $optimizedTableForPathQueryMgAndSub | Sort-Object -Property Subscription
- $summarySubscriptionsCount = ($summarySubscriptions).Count
-
- $arrayPIMEligibleGroupedBySubscription = $arrayPIMEligible.where({ $_.ScopeType -eq 'Sub' }) | Group-Object -Property ScopeId
-
- if ($summarySubscriptionsCount -gt 0) {
-
- $advisorScoreCategories = $arrayAdvisorScores.category | Sort-Object -Unique
- $htAdvisorScoresSubscriptions = @{}
- if ($advisorScoreCategories.Count -gt 0) {
- $arrayAdvisorScoresGroupedBySubscriptionId = $arrayAdvisorScores | Group-Object -Property subscriptionId
- foreach ($subEntry in $arrayAdvisorScoresGroupedBySubscriptionId) {
- $htAdvisorScoresSubscriptions.($subEntry.Name) = @{}
- foreach ($possibleCategory in $advisorScoreCategories) {
- if ($subEntry.Group.category -eq $possibleCategory) {
- $htAdvisorScoresSubscriptions.($subEntry.Name).($possibleCategory) = $subEntry.Group.where({ $_.category -eq $possibleCategory }).score
- }
- }
- }
- }
-
- $tfCount = $summarySubscriptionsCount
- $htmlTableId = 'TenantSummary_subs'
- $abbr = "
"
- [void]$htmlTenantSummary.AppendLine(@"
-
$($summarySubscriptionsCount) Subscriptions (state: enabled)
-
-
Supported Microsoft Azure offers docs
-
Understand Microsoft Defender for Cloud Secure Score Video ,
Blog ,
docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-QuotaId
-Role assignment limit
-Tags
-Owner (at Scope) direct
-Owner (at Scope) indirect$($abbr)
-Owner (PIM eligible at scope)
-User Access Administrator (at Scope) direct
-User Access Administrator (at Scope) indirect$($abbr)
-User Access Administrator (PIM eligible at scope)
-MDfC Score
-MDfC 'Email notifications' state
-MDfC 'Email notifications' severity
-MDfC 'Email notifications' roles
-MDfC 'Email notifications' emails
-"@)
-
- foreach ($possibleCategory in $advisorScoreCategories) {
- if ($possibleCategory -eq 'Advisor') {
- [void]$htmlTenantSummary.AppendLine(@"
- $possibleCategory score
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
- Advisor $possibleCategory score
-"@)
- }
- }
-
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- [void]$htmlTenantSummary.AppendLine(@"
-Cost ($($AzureConsumptionPeriod)d)
-Currency
-"@)
- }
- [void]$htmlTenantSummary.AppendLine(@'
-Management Group Path
-
-
-
-'@)
-
- if (-not $ManagementGroupsOnly) {
- if (-not $NoCsvExport) {
- Write-Host " Exporting MDfC Email Notifications CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName)_MDfCEmailNotifications.csv'"
- $htDefenderEmailContacts.values | Sort-Object -Property subscriptionName | Select-Object -Property subscriptionId, subscriptionName, alertNotificationsState, alertNotificationsminimalSeverity, roles, emails | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_MDfCEmailNotifications.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
- }
-
- $subscriptionDetails4CSVExport = [System.Collections.ArrayList]@()
- $htmlSUMMARYSubs = $null
- $htmlSUMMARYSubs = foreach ($summarySubscription in $summarySubscriptions) {
- $subPath = $htSubscriptionsMgPath.($summarySubscription.subscriptionId).ParentNameChainDelimited
- $subscriptionTagsArray = [System.Collections.ArrayList]@()
- foreach ($tag in ($htSubscriptionTags).($summarySubscription.subscriptionId).keys) {
- $null = $subscriptionTagsArray.Add("'$($tag)':'$(($htSubscriptionTags).$($summarySubscription.subscriptionId).$tag)'")
- }
-
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- if ($htAzureConsumptionSubscriptions.($summarySubscription.subscriptionId)) {
- if ([math]::Round($htAzureConsumptionSubscriptions.($summarySubscription.subscriptionId).TotalCost, 2) -eq 0) {
- $totalCost = $htAzureConsumptionSubscriptions.($summarySubscription.subscriptionId).TotalCost.ToString('0.0000')
- }
- else {
- $totalCost = (([math]::Round($htAzureConsumptionSubscriptions.($summarySubscription.subscriptionId).TotalCost, 2))).ToString('0.00')
- }
- $currency = $htAzureConsumptionSubscriptions.($summarySubscription.subscriptionId).Currency
- }
- else {
- $totalCost = '0'
- $currency = 'n/a'
- }
- }
- else {
- $totalCost = 'n/a'
- $currency = 'n/a'
- }
-
- if ($htDefenderEmailContacts.($summarySubscription.subscriptionId)) {
- $hlpDefenderEmailContacts = $htDefenderEmailContacts.($summarySubscription.subscriptionId)
- $MDfCEmailNotificationsState = $hlpDefenderEmailContacts.alertNotificationsState
- $MDfCEmailNotificationsSeverity = $hlpDefenderEmailContacts.alertNotificationsminimalSeverity
- $MDfCEmailNotificationsRoles = $hlpDefenderEmailContacts.roles
- $MDfCEmailNotificationsEmails = $hlpDefenderEmailContacts.emails
- }
- else {
- $MDfCEmailNotificationsState = ''
- $MDfCEmailNotificationsSeverity = ''
- $MDfCEmailNotificationsRoles = ''
- $MDfCEmailNotificationsEmails = ''
- }
-
- #rbac assignments owner and userAccountAdministrator
- $rbacAtScopeForThisSubscription = ($rbacAllGroupedBySubscription.where( { $_.name -eq $summarySubscription.subscriptionId } )).group
-
- $rbacOwnersAtScopeForThisSubscription = ($rbacAtScopeForThisSubscription.where({ $_.Scope -eq 'thisScope Sub' -and $_.RoleId -eq '8e3af657-a8ff-443c-a75c-2fe8c4bcb635' }))
- $rbacOwnersAtScopeForThisSubscriptionDirectCount = ($rbacOwnersAtScopeForThisSubscription.where( { $_.AssignmentType -eq 'direct' } )).Count
- $rbacOwnersAtScopeForThisSubscriptionInDirectCount = $rbacOwnersAtScopeForThisSubscription.Count - $rbacOwnersAtScopeForThisSubscriptionDirectCount
-
- $rbacUAAsAtScopeForThisSubscription = ($rbacAtScopeForThisSubscription.where({ $_.Scope -eq 'thisScope Sub' -and $_.RoleId -eq '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' }))
- $rbacUAAsAtScopeForThisSubscriptionDirectCount = ($rbacUAAsAtScopeForThisSubscription.where( { $_.AssignmentType -eq 'direct' } )).Count
- $rbacUAAsAtScopeForThisSubscriptionInDirectCount = $rbacUAAsAtScopeForThisSubscription.Count - $rbacUAAsAtScopeForThisSubscriptionDirectCount
-
- #pim eligibility owner and userAccountAdministrator
- $pimEligibleOwnersAtScopeForThisSubscriptionCount = ''
- $pimEligibleUAAsAtScopeForThisSubscriptionCount = ''
- if (-not $NoPIMEligibility) {
- $pimEligibleAtScopeForThisSubscription = ($arrayPIMEligibleGroupedBySubscription.where( { $_.name -eq $summarySubscription.subscriptionId } )).group
- $pimEligibleOwnersAtScopeForThisSubscriptionCount = ($pimEligibleAtScopeForThisSubscription.where( { $_.RoleIdGuid -eq '8e3af657-a8ff-443c-a75c-2fe8c4bcb635' } )).Count
- $pimEligibleUAAsAtScopeForThisSubscriptionCount = ($pimEligibleAtScopeForThisSubscription.where( { $_.RoleIdGuid -eq '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' } )).Count
- }
-
- $htColl = [ordered]@{}
- @"
-
-$($summarySubscription.subscription -replace '<', '<' -replace '>', '>')
-$($summarySubscription.subscriptionId)
-$($summarySubscription.SubscriptionQuotaId)
-$($htSubscriptionsRoleAssignmentLimit.($summarySubscription.subscriptionId))
-$(($subscriptionTagsArray | Sort-Object) -join "$CsvDelimiterOpposite ")
-$($rbacOwnersAtScopeForThisSubscriptionDirectCount)
-$($rbacOwnersAtScopeForThisSubscriptionInDirectCount)
-$($pimEligibleOwnersAtScopeForThisSubscriptionCount)
-$($rbacUAAsAtScopeForThisSubscriptionDirectCount)
-$($rbacUAAsAtScopeForThisSubscriptionInDirectCount)
-$($pimEligibleUAAsAtScopeForThisSubscriptionCount)
-$($summarySubscription.SubscriptionASCSecureScore)
-$($MDfCEmailNotificationsState)
-$($MDfCEmailNotificationsSeverity)
-$($MDfCEmailNotificationsRoles)
-$($MDfCEmailNotificationsEmails)
-"@
-
- $htColl.Subscription = $summarySubscription.subscription
- $htColl.SubscriptionId = $summarySubscription.subscriptionId
- $htColl.QuotaId = $summarySubscription.SubscriptionQuotaId
- $htColl.ManagementGroupPath = $subPath
- $htColl.RoleAssignmentLimit = $htSubscriptionsRoleAssignmentLimit.($summarySubscription.subscriptionId)
- $htColl.Tags = ($subscriptionTagsArray | Sort-Object) -join "$CsvDelimiterOpposite "
- $htColl.'Owner(atScope)Direct' = $rbacOwnersAtScopeForThisSubscriptionDirectCount
- $htColl.'Owner(atScope)Indirect' = $rbacOwnersAtScopeForThisSubscriptionInDirectCount
- $htColl.'Owner(PIMEligibleAtScope)' = $pimEligibleOwnersAtScopeForThisSubscriptionCount
- $htColl.'UserAccessAdministrator(atScope)Direct' = $rbacUAAsAtScopeForThisSubscriptionDirectCount
- $htColl.'UserAccessAdministrator(atScope)Indirect' = $rbacUAAsAtScopeForThisSubscriptionInDirectCount
- $htColl.'UserAccessAdministrator(PIMEligibleAtScope)' = $pimEligibleUAAsAtScopeForThisSubscriptionCount
- $htColl.MDfCScore = $summarySubscription.SubscriptionASCSecureScore
- $htColl.MDfCEmailNotificationsState = $MDfCEmailNotificationsState
- $htColl.MDfCEmailNotificationsSeverity = $MDfCEmailNotificationsSeverity
- $htColl.MDfCEmailNotificationsRoles = $MDfCEmailNotificationsRoles
- $htColl.MDfCEmailNotificationsEmails = $MDfCEmailNotificationsEmails
-
- foreach ($possibleCategory in $advisorScoreCategories) {
- if ($htAdvisorScoresSubscriptions.($summarySubscription.subscriptionId).($possibleCategory)) {
- @"
- $([math]::Round(($htAdvisorScoresSubscriptions.($summarySubscription.subscriptionId).($possibleCategory)), 2))
-"@
- if ($possibleCategory -eq 'Advisor') {
- $htColl.("$($possibleCategory)Score") = $htAdvisorScoresSubscriptions.($summarySubscription.subscriptionId).($possibleCategory)
- }
- else {
- $htColl.("Advisor$($possibleCategory)Score") = $htAdvisorScoresSubscriptions.($summarySubscription.subscriptionId).($possibleCategory)
- }
- }
- else {
- @'
- n/a
-'@
- $htColl.($possibleCategory) = 'n/a'
- }
- }
-
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- @"
-$totalCost
-$currency
-"@
- $htColl."Cost($($AzureConsumptionPeriod)d)" = $totalCost
- $htColl.Currency = $currency
- }
- @"
- $subPath
-
-"@
- if (-not $NoCsvExport) {
- $null = $subscriptionDetails4CSVExport.Add($htColl)
- }
- }
-
- if (-not $ManagementGroupsOnly) {
- if (-not $NoCsvExport) {
- Write-Host " Exporting SubscriptionDetails CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName)_SubscriptionDetails.csv'"
- $subscriptionDetails4CSVExport | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_SubscriptionDetails.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
- }
-
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSubs)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($summarySubscriptionsCount) Subscriptions
-"@)
- }
-
- $endSUMMARYSubs = Get-Date
- Write-Host " SUMMARYSubs duration: $((New-TimeSpan -Start $startSUMMARYSubs -End $endSUMMARYSubs).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYSubs -End $endSUMMARYSubs).TotalSeconds) seconds)"
- #endregion SUMMARYSubs
-
- #region SUMMARYOutOfScopeSubscriptions
- Write-Host ' processing TenantSummary Subscriptions (out-of-scope)'
- $outOfScopeSubscriptionsCount = ($outOfScopeSubscriptions).Count
- if ($outOfScopeSubscriptionsCount -gt 0) {
- $tfCount = $outOfScopeSubscriptionsCount
- $htmlTableId = 'TenantSummary_outOfScopeSubscriptions'
- [void]$htmlTenantSummary.AppendLine(@"
-
$outOfScopeSubscriptionsCount Subscriptions out-of-scope
-
-
Download CSV
semicolon |
comma
-
-
-
-Subscription Name
-SubscriptionId
-out-of-scope reason
-Management Group
-
-
-
-"@)
- $htmlSUMMARYOutOfScopeSubscriptions = $null
- $htmlSUMMARYOutOfScopeSubscriptions = foreach ($outOfScopeSubscription in $outOfScopeSubscriptions) {
- @"
-
-$($outOfScopeSubscription.SubscriptionName)
-$($outOfScopeSubscription.SubscriptionId)
-$($outOfScopeSubscription.outOfScopeReason)
- $($outOfScopeSubscription.ManagementGroupName -replace '<', '<' -replace '>', '>') ($($outOfScopeSubscription.ManagementGroupId))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYOutOfScopeSubscriptions)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$outOfScopeSubscriptionsCount Subscriptions out-of-scope
-"@)
- }
- #endregion SUMMARYOutOfScopeSubscriptions
-
- #region SUMMARYTagNameUsage
- Write-Host ' processing TenantSummary TagsUsage'
- $tagsUsageCount = ($arrayTagList).Count
- if ($tagsUsageCount -gt 0) {
- $tagNamesUniqueCount = ($arrayTagList | Sort-Object -Property TagName -Unique).Count
- $tagNamesUsedInScopes = ($arrayTagList.where( { $_.Scope -ne 'AllScopes' }) | Sort-Object -Property Scope -Unique).scope -join "$($CsvDelimiterOpposite) "
- $tfCount = $tagsUsageCount
- $htmlTableId = 'TenantSummary_tagsUsage'
- [void]$htmlTenantSummary.AppendLine(@"
-
Tag Name Usage ($tagNamesUniqueCount unique Tag Names applied at $($tagNamesUsedInScopes))
-
-
Resource naming and tagging decision guide docs
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-TagName
-Count
-
-
-
-"@)
- $htmlSUMMARYtagsUsage = $null
- $htmlSUMMARYtagsUsage = foreach ($tagEntry in $arrayTagList | Sort-Object -Property Scope, TagName -CaseSensitive) {
- @"
-
-$($tagEntry.Scope)
-$($tagEntry.TagName)
-$($tagEntry.TagCount)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYtagsUsage)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Tag Name Usage ($tagsUsageCount Tags) docs
-"@)
- }
- #endregion SUMMARYTagNameUsage
-
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- #region SUMMARYResources
- $startSUMMARYResources = Get-Date
- Write-Host ' processing TenantSummary Subscriptions Resources'
- if (($resourcesAll).count -gt 0) {
- $resourcesAllGroupedByType = $resourcesAll | Select-Object -Property type, count_ | Group-Object type
- $resourcesTotal = ($resourcesAll.count_ | Measure-Object -Sum).Sum
- $resourcesResourceTypeCount = ($resourcesAll.type | Sort-Object -Unique).Count
-
- if ($resourcesResourceTypeCount -gt 0) {
- $tfCount = ($resourcesAllGroupedByType | Measure-Object).Count
- $htmlTableId = 'TenantSummary_resources'
- [void]$htmlTenantSummary.AppendLine(@"
-
Resources ($resourcesResourceTypeCount ResourceTypes) ($resourcesTotal Resources) ($scopeNamingSummary)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Resource Count
-
-
-
-"@)
- $htmlSUMMARYResources = $null
- $htmlSUMMARYResources = foreach ($resourceAllSummarized in $resourcesAllGroupedByType) {
- $type = $resourceAllSummarized.Name
- $script:htDailySummary."ResourceType_$($resourceAllSummarized.Name)" = ($resourceAllSummarized.group.count_ | Measure-Object -Sum).Sum
- @"
-
-$($type)
-$(($resourceAllSummarized.group.count_ | Measure-Object -Sum).Sum)
-
-"@
-
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYResources)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Resources ($resourcesResourceTypeCount ResourceTypes)
-"@)
- }
-
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
Resources (0 ResourceTypes)
-'@)
- }
- $endSUMMARYResources = Get-Date
- Write-Host " SUMMARY Resources processing duration: $((New-TimeSpan -Start $startSUMMARYResources -End $endSUMMARYResources).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYResources -End $endSUMMARYResources).TotalSeconds) seconds)"
- #endregion SUMMARYResources
-
- #region SUMMARYResourcesByLocation
- $startSUMMARYResources = Get-Date
- Write-Host ' processing TenantSummary Subscriptions Resources by Location'
- if (($resourcesAll | Measure-Object).count -gt 0) {
- $resourcesAllGroupedByTypeLocation = $resourcesAll | Select-Object -Property type, location, count_ | Group-Object type, location
- $resourcesTotal = ($resourcesAll.count_ | Measure-Object -Sum).Sum
- $resourcesResourceTypeCount = ($resourcesAll.type | Sort-Object -Unique).Count
- $resourcesLocationCount = ($resourcesAll.location | Sort-Object -Unique).Count
-
- if ($resourcesResourceTypeCount -gt 0) {
- $tfCount = ($resourcesAllGroupedByTypeLocation | Measure-Object).Count
- $htmlTableId = 'TenantSummary_resourcesByLocation'
- [void]$htmlTenantSummary.AppendLine(@"
-
Resources byLocation ($resourcesResourceTypeCount ResourceTypes) ($resourcesTotal Resources) in $resourcesLocationCount Locations ($scopeNamingSummary)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Location
-Resource Count
-
-
-
-"@)
- $htmlSUMMARYResources = $null
- $htmlSUMMARYResources = foreach ($resourceAllSummarized in $resourcesAllGroupedByTypeLocation) {
- $typeLocation = $resourceAllSummarized.Name.Split(', ')
- $type = $typeLocation[0]
- $location = $typeLocation[1]
- @"
-
-$($type)
-$($location)
-$(($resourceAllSummarized.group.count_ | Measure-Object -Sum).Sum)
-
-"@
-
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYResources)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Resources ($resourcesResourceTypeCount ResourceTypes)
-"@)
- }
-
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
Resources (0 ResourceTypes)
-'@)
- }
- $endSUMMARYResources = Get-Date
- Write-Host " SUMMARY Resources ByLocation processing duration: $((New-TimeSpan -Start $startSUMMARYResources -End $endSUMMARYResources).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYResources -End $endSUMMARYResources).TotalSeconds) seconds)"
- #endregion SUMMARYResourcesByLocation
-
- #region SUMMARYResourceFluctuation
- $startSUMMARYResourceFluctuation = Get-Date
- Write-Host ' processing TenantSummary Resource fluctuation'
- if (($arrayResourceFluctuationFinal).count -gt 0) {
- $resourceTypesCount = ($arrayResourceFluctuationFinal | Group-Object -Property ResourceType | Measure-Object).Count
- $addedCount = ($arrayResourceFluctuationFinal.where({ $_.Event -eq 'Added' }).'Resource count' | Measure-Object -Sum).Sum
- $removedCount = ($arrayResourceFluctuationFinal.where({ $_.Event -eq 'Removed' }).'Resource count' | Measure-Object -Sum).Sum
-
- $tfCount = ($arrayResourceFluctuationFinal).count
- $htmlTableId = 'TenantSummary_resourceFluctuation'
- [void]$htmlTenantSummary.AppendLine(@"
-
Resource fluctuation - $resourceTypesCount Resource types (Resources: $addedCount added, $removedCount removed)
-
-
-
Download CSV
semicolon |
comma
-
-
-
-Event
-ResourceType
-Resource count
-Subscription count
-
-
-
-"@)
- $htmlSUMMARYResourceFluctuation = $null
- $htmlSUMMARYResourceFluctuation = foreach ($entry in $arrayResourceFluctuationFinal | Sort-Object -Property ResourceType, Event) {
- @"
-
-$($entry.Event)
-$($entry.ResourceType)
-$($entry.'Resource count')
-$($entry.'Subscription count')
-
-"@
-
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYResourceFluctuation)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No Resource fluctuation since last run
-'@)
- }
- $endSUMMARYResourceFluctuation = Get-Date
- Write-Host " SUMMARY Resource fluctuation processing duration: $((New-TimeSpan -Start $startSUMMARYResourceFluctuation -End $endSUMMARYResourceFluctuation).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYResourceFluctuation -End $endSUMMARYResourceFluctuation).TotalSeconds) seconds)"
- #endregion SUMMARYResourceFluctuation
-
- #region SUMMARYCAFResourceNamingALL
- $startSUMMARYCAFResourceNamingALL = Get-Date
- Write-Host ' processing TenantSummary CAFResourceNamingALL'
- $script:resourcesIdsAllCAFNamingRelevant = $resourcesIdsAll.where({ $_.cafResourceNamingResult -ne 'n/a' })
- $resourcesIdsAllCAFNamingRelevantGroupedByType = $resourcesIdsAllCAFNamingRelevant | Group-Object -Property type
- $resourcesIdsAllCAFNamingRelevantGroupedByTypeCount = ($resourcesIdsAllCAFNamingRelevantGroupedByType | Measure-Object).Count
-
- if ($resourcesIdsAllCAFNamingRelevantGroupedByTypeCount -gt 0) {
-
- $tfCount = $resourcesIdsAllCAFNamingRelevantGroupedByTypeCount
- $htmlTableId = 'TenantSummary_CAFResourceNamingALL'
- [void]$htmlTenantSummary.AppendLine(@"
-
CAF Naming Recommendation Compliance
-
-
-
CAF - Recommended abbreviations for Azure resource types docs
-
Resource details can be found in the CSV output *_ResourcesAll.csv
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Recommendation
-ResourceFriendlyName
-passed
-failed
-passed percentage
-
-
-
-"@)
-
- $htmlSUMMARYCAFResourceNamingALL = $null
- $htmlSUMMARYCAFResourceNamingALL = foreach ($entry in $resourcesIdsAllCAFNamingRelevantGroupedByType) {
-
- $resourceTypeGroupedByCAFResourceNamingResult = $entry.Group | Group-Object -Property cafResourceNamingResult, cafResourceNaming
- if ($entry.Group.cafResourceNaming.Count -gt 1) {
- $namingConvention = ($entry.Group.cafResourceNaming)[0]
- $namingConventionFriendlyName = ($entry.Group.cafResourceNamingFriendlyName)[0]
- }
- else {
- $namingConvention = $entry.Group.cafResourceNaming
- $namingConventionFriendlyName = $entry.Group.cafResourceNamingFriendlyName
- }
-
- $passed = 0
- $failed = 0
- foreach ($result in $resourceTypeGroupedByCAFResourceNamingResult) {
- $resultNameSplitted = $result.Name -split ', '
- if ($resultNameSplitted[0] -eq 'passed') {
- $passed = $result.Count
- }
-
- if ($resultNameSplitted[0] -eq 'failed') {
- $failed = $result.Count
- }
- }
-
- if ($passed -gt 0) {
- $percentage = [math]::Round(($passed / ($passed + $failed) * 100), 2)
- }
- else {
- $percentage = 0
- }
-
- @"
-
-$($entry.Name)
-$($namingConvention)
-$($namingConventionFriendlyName)
-$($passed)
-$($failed)
-$($percentage)%
-
-"@
-
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYCAFResourceNamingALL)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No CAF Naming Recommendation Compliance data
-'@)
- }
- $endSUMMARYCAFResourceNamingALL = Get-Date
- Write-Host " SUMMARY CAFResourceNamingALL processing duration: $((New-TimeSpan -Start $startSUMMARYCAFResourceNamingALL -End $endSUMMARYCAFResourceNamingALL).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYCAFResourceNamingALL -End $endSUMMARYCAFResourceNamingALL).TotalSeconds) seconds)"
- #endregion SUMMARYCAFResourceNamingALL
- }
-
- #region SUMMARYOrphanedResources
- $startSUMMARYOrphanedResources = Get-Date
- Write-Host ' processing TenantSummary Orphaned/unused Resources'
- if ($arrayOrphanedResources.count -gt 0) {
- $script:arrayOrphanedResourcesSlim = $arrayOrphanedResources | Sort-Object -Property type
-
-
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- $orphanedIncludingCost = $true
- $hintTableTH = " ($($AzureConsumptionPeriod) days)"
-
- $arrayOrphanedResourcesGroupedByType = $arrayOrphanedResourcesSlim | Group-Object type, currency
- $orphanedResourceTypesCount = ($arrayOrphanedResourcesGroupedByType | Measure-Object).Count
- $orphanedResourceTypesCountUnique = ($arrayOrphanedResourcesSlim.type | Sort-Object -Unique).Count
- }
- else {
- $orphanedIncludingCost = $false
- $hintTableTH = ''
-
- $arrayOrphanedResourcesGroupedByType = $arrayOrphanedResourcesSlim | Group-Object type
- $orphanedResourceTypesCount = ($arrayOrphanedResourcesGroupedByType | Measure-Object).Count
- $orphanedResourceTypesCountUnique = ($arrayOrphanedResourcesSlim.type | Sort-Object -Unique).Count
- }
-
- $tfCount = $orphanedResourceTypesCount
- $htmlTableId = 'TenantSummary_orphanedResources'
- [void]$htmlTenantSummary.AppendLine(@"
-
= Cost optimization & cleanup - $($arrayOrphanedResources.count) Resources, $orphanedResourceTypesCountUnique Resource Types
-
-
-
'Azure Orphan Resources' ARG queries and workbooks GitHub
-
Resource details can be found in the CSV output *_ResourcesCostOptimizationAndCleanup.csv
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Resource count
-Subscriptions count
-Intent
-Cost$($hintTableTH)
-Currency
-
-
-
-"@)
-
- $htmlSUMMARYOrphanedResources = $null
- $htmlSUMMARYOrphanedResources = foreach ($orphanedResourceType in $arrayOrphanedResourcesGroupedByType | Sort-Object -Property Name) {
- $script:htDailySummary."OrpanedResourceType_$($orphanedResourceType.Name)" = ($orphanedResourceType.count)
- if ($orphanedIncludingCost) {
- if (($orphanedResourceType.Group[0].Intent) -like 'cost savings*') {
- $orphCost = ($orphanedResourceType.Group.Cost | Measure-Object -Sum).Sum
- if ($orphCost -eq 0) {
- $orphCost = ''
- }
- $orphCurrency = $orphanedResourceType.Group[0].Currency
- $script:htDailySummary."OrpanedResourceType_$($orphanedResourceType.Name)_Costs" = $orphCost
- $script:htDailySummary."OrpanedResourceType_$($orphanedResourceType.Name)_Costs_ConsumptionPeriodInDays" = $AzureConsumptionPeriod
- }
- else {
- $orphCost = ''
- $orphCurrency = ''
- }
-
- }
- else {
- if (($orphanedResourceType.Group.Intent | Get-Unique) -like 'cost savings*') {
- $orphCost = "use parameter -DoAzureConsumption to show potential savings "
- $orphCurrency = ''
- }
- else {
- $orphCost = ''
- $orphCurrency = ''
- }
- }
-
- @"
-
-$(($orphanedResourceType.Name -split ',')[0])
-$($orphanedResourceType.count)
-$(($orphanedResourceType.Group.SubscriptionId | Sort-Object -Unique).Count)
-$($orphanedResourceType.Group[0].Intent)
-$($orphCost)
-$($orphCurrency)
-
-"@
-
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYOrphanedResources)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No cost optimization & cleanup
-'@)
- }
- $endSUMMARYOrphanedResources = Get-Date
- Write-Host " SUMMARY Orphaned/unused Resources processing duration: $((New-TimeSpan -Start $startSUMMARYOrphanedResources -End $endSUMMARYOrphanedResources).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYOrphanedResources -End $endSUMMARYOrphanedResources).TotalSeconds) seconds)"
- #endregion SUMMARYOrphanedResources
-
- #region SUMMARYSubResourceProviders
- if ($azAPICallConf['htParameters'].NoResourceProvidersAtAll -eq $false) {
- $startSUMMARYSubResourceProviders = Get-Date
- Write-Host ' processing TenantSummary Subscriptions Resource Providers'
- $resourceProvidersAllCount = (($htResourceProvidersAll).Keys | Measure-Object).count
- if ($resourceProvidersAllCount -gt 0) {
- $grped = (($htResourceProvidersAll).values.Providers) | Sort-Object -Property namespace, registrationState | Group-Object namespace
- $htResProvSummary = @{}
- foreach ($grp in $grped) {
- $htResProvSummary.($grp.name) = @{}
- $regstates = ($grp.group | Sort-Object -Property registrationState -Unique).registrationstate
- foreach ($regstate in $regstates) {
- $htResProvSummary.($grp.name).$regstate = (($grp.group).where( { $_.registrationstate -eq $regstate }) | Measure-Object).count
- }
- }
- $providerSummary = [System.Collections.ArrayList]@()
- foreach ($provider in $htResProvSummary.keys) {
- $hlperProvider = $htResProvSummary.$provider
- if ($hlperProvider.registered) {
- $registered = $hlperProvider.registered
- }
- else {
- $registered = '0'
- }
-
- if ($hlperProvider.registering) {
- $registering = $hlperProvider.registering
- }
- else {
- $registering = '0'
- }
-
- if ($hlperProvider.notregistered) {
- $notregistered = $hlperProvider.notregistered
- }
- else {
- $notregistered = '0'
- }
-
- if ($hlperProvider.unregistering) {
- $unregistering = $hlperProvider.unregistering
- }
- else {
- $unregistering = '0'
- }
-
- $null = $providerSummary.Add([PSCustomObject]@{
- Provider = $provider
- Registered = $registered
- NotRegistered = $notregistered
- Registering = $registering
- Unregistering = $unregistering
- })
- }
-
- $uniqueNamespaces = (($htResourceProvidersAll).values.Providers) | Sort-Object -Property namespace -Unique
- $uniqueNamespacesCount = ($uniqueNamespaces | Measure-Object).count
- $uniqueNamespaceRegistrationState = (($htResourceProvidersAll).values.Providers) | Sort-Object -Property namespace, registrationState -Unique
- $providersRegistered = ($uniqueNamespaceRegistrationState.where( { $_.registrationState -eq 'registered' -or $_.registrationState -eq 'registering' }) | Sort-Object namespace -Unique).namespace
- $providersRegisteredCount = ($providersRegistered | Measure-Object).count
-
- $providersNotRegisteredUniqueCount = 0
- foreach ($uniqueNamespace in $uniqueNamespaces) {
- if ($providersRegistered -notcontains ($uniqueNamespace.namespace)) {
- $providersNotRegisteredUniqueCount++
- }
- }
- $tfCount = $uniqueNamespacesCount
- $htmlTableId = 'TenantSummary_SubResourceProviders'
- [void]$htmlTenantSummary.AppendLine(@"
-
Resource Providers Total: $uniqueNamespacesCount Registered/Registering: $providersRegisteredCount NotRegistered/Unregistering: $providersNotRegisteredUniqueCount
-
-
Download CSV
semicolon |
comma
-
-
-
-Provider
-Registered
-Registering
-NotRegistered
-Unregistering
-
-
-
-"@)
- $htmlSUMMARYSubResourceProviders = $null
- $htmlSUMMARYSubResourceProviders = foreach ($provider in ($providerSummary | Sort-Object -Property Provider)) {
- @"
-
-$($provider.Provider)
-$($provider.Registered)
-$($provider.Registering)
-$($provider.NotRegistered)
-$($provider.Unregistering)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSubResourceProviders)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$resourceProvidersAllCount Resource Providers
-"@)
- }
- $endSUMMARYSubResourceProviders = Get-Date
- Write-Host " TenantSummary Subscriptions Resource Providers duration: $((New-TimeSpan -Start $startSUMMARYSubResourceProviders -End $endSUMMARYSubResourceProviders).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYSubResourceProviders -End $endSUMMARYSubResourceProviders).TotalSeconds) seconds)"
- }
- #endregion SUMMARYSubResourceProviders
-
- #region SUMMARYSubResourceProvidersDetailed
- if ($azAPICallConf['htParameters'].NoResourceProvidersAtAll -eq $false) {
- if ($azAPICallConf['htParameters'].NoResourceProvidersDetailed -eq $false) {
-
- Write-Host ' processing TenantSummary Subscriptions Resource Providers detailed'
- $startsumRPDetailed = Get-Date
- $resourceProvidersAllCount = (($htResourceProvidersAll).Keys).count
- if ($resourceProvidersAllCount -gt 0) {
- $tfCount = ($htResourceProvidersAll).values.Providers.Count
- if ($tfCount -lt $HtmlTableRowsLimit) {
- $htmlTableId = 'TenantSummary_SubResourceProvidersDetailed'
- [void]$htmlTenantSummary.AppendLine(@"
-
Resource Providers Detailed
-
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-Subscription MG path
- Provider
-State
-
-
-
-"@)
-
- }
- else {
- Write-Host " !Skipping TenantSummary ResourceProvidersDetailed HTML processing as $tfCount lines is exceeding the critical rows limit of $HtmlTableRowsLimit" -ForegroundColor Yellow
- }
- $cnter = 0
- $startResProvDetailed = Get-Date
- $htmlSUMMARYSubResourceProvidersDetailed = $null
-
- $arrayResourceProvidersDetailedForCSVExport = [System.Collections.ArrayList]@()
- $htmlSUMMARYSubResourceProvidersDetailed = foreach ($subscriptionResProv in (($htResourceProvidersAll).Keys | Sort-Object)) {
- $subscriptionResProvDetails = $htSubscriptionsMgPath.($subscriptionResProv)
- foreach ($provider in ($htResourceProvidersAll).($subscriptionResProv).Providers | Sort-Object @{Expression = { $_.namespace } }) {
- $cnter++
- if ($cnter % 1000 -eq 0) {
- $etappeResProvDetailed = Get-Date
- Write-Host " $cnter ResProv processed; $((New-TimeSpan -Start $startResProvDetailed -End $etappeResProvDetailed).TotalSeconds) seconds"
- }
-
- #array for exportCSV
- if (-not $NoCsvExport) {
- $null = $arrayResourceProvidersDetailedForCSVExport.Add([PSCustomObject]@{
- Subscription = $subscriptionResProvDetails.DisplayName
- SubscriptionId = $subscriptionResProv
- SubscriptionMGpath = $subscriptionResProvDetails.pathDelimited
- Provider = $provider.namespace
- State = $provider.registrationState
- })
- }
-
- @"
-
-$($subscriptionResProvDetails.DisplayName)
-$($subscriptionResProv)
-$($subscriptionResProvDetails.pathDelimited)
-$($provider.namespace)
-$($provider.registrationState)
-
-"@
- }
- }
-
- #region exportCSV
- if (-not $NoCsvExport) {
- $csvFilename = "$($filename)_ResourceProviders"
- Write-Host " Exporting ResourceProviders CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
- $arrayResourceProvidersDetailedForCSVExport | Export-Csv -Encoding utf8 -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter $csvDelimiter -NoTypeInformation
- $arrayResourceProvidersDetailedForCSVExport = $null
- }
- #endregion exportCSV
-
- if ($tfCount -lt $HtmlTableRowsLimit) {
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSubResourceProvidersDetailed)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Resource Providers Detailed
-
-
Output of $tfCount lines would exceed the html rows limit of $HtmlTableRowsLimit (html file potentially would become unresponsive). Work with the CSV file $($csvFilename).csv | Note: the CSV file will only exist if you did NOT use parameter -NoCsvExport
-
You can adjust the html row limit by using parameter -HtmlTableRowsLimit
-
Check the parameters documentation Azure Governance Visualizer docs
-
-"@)
- }
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$resourceProvidersAllCount Resource Providers
-"@)
- }
- $endsumRPDetailed = Get-Date
- Write-Host " RP detailed processing duration: $((New-TimeSpan -Start $startsumRPDetailed -End $endsumRPDetailed).TotalMinutes) minutes ($((New-TimeSpan -Start $startsumRPDetailed -End $endsumRPDetailed).TotalSeconds) seconds)"
- }
- }
- #endregion SUMMARYSubResourceProvidersDetailed
-
- #region SUMMARYSubFeatures
- Write-Host ' processing TenantSummary Subscriptions Features'
- $startSubFeatures = Get-Date
- $subFeaturesAllCount = $arrayFeaturesAll.count
- if ($subFeaturesAllCount -gt 0) {
-
- #region exportCSV
- if (-not $NoCsvExport) {
- $csvFilename = "$($filename)_SubscriptionsFeatures"
- Write-Host " Exporting SubscriptionsFeatures CSV '$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv'"
- ($arrayFeaturesAll | Select-Object -ExcludeProperty mgPathArray | Sort-Object -Property feature, subscriptionId) | Export-Csv -Encoding utf8 -Path "$($outputPath)$($DirectorySeparatorChar)$($csvFilename).csv" -Delimiter $csvDelimiter -NoTypeInformation
- }
- #endregion exportCSV
-
- $subFeaturesGroupedByFeature = $arrayFeaturesAll | Group-Object -Property feature
- $tfCount = ($subFeaturesGroupedByFeature | Measure-Object).Count
- $htmlTableId = 'TenantSummary_SubFeatures'
- [void]$htmlTenantSummary.AppendLine(@"
-
$tfCount enabled Subscriptions Features
-
-
Set up preview features in Azure subscription docs
-
Download CSV
semicolon |
comma
-
-
-
-Feature
-Subscriptions
-
-
-
-"@)
-
-
- $cnter = 0
- $startResProvDetailed = Get-Date
- $htmlSUMMARYSubFeatures = $null
- $htmlSUMMARYSubFeatures = foreach ($feature in $subFeaturesGroupedByFeature | Sort-Object -Property name) {
- @"
-
-$($feature.name)
-$($feature.Count)
-
-"@
- }
-
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSubFeatures)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
-
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No enabled Subscriptions Features docs
-'@)
- }
- $endSubFeatures = Get-Date
- Write-Host " Subscriptions Features processing duration: $((New-TimeSpan -Start $startSubFeatures -End $endSubFeatures).TotalMinutes) minutes ($((New-TimeSpan -Start $startSubFeatures -End $endSubFeatures).TotalSeconds) seconds)"
- #endregion SUMMARYSubFeatures
-
- #region SUMMARYSubResourceLocks
- Write-Host ' processing TenantSummary Subscriptions Resource Locks'
- $tfCount = 6
- $startResourceLocks = Get-Date
-
- if (($htResourceLocks.keys | Measure-Object).Count -gt 0) {
- $htmlTableId = 'TenantSummary_ResourceLocks'
-
- $subscriptionLocksCannotDeleteCount = ($htResourceLocks.Keys.where( { $htResourceLocks.($_).SubscriptionLocksCannotDeleteCount -gt 0 } )).Count
- $subscriptionLocksReadOnlyCount = ($htResourceLocks.Keys.where( { $htResourceLocks.($_).SubscriptionLocksReadOnlyCount -gt 0 } )).Count
-
- $resourceGroupsLocksCannotDeleteCount = ($htResourceLocks.Keys.where( { $htResourceLocks.($_).ResourceGroupsLocksCannotDeleteCount -gt 0 } )).Count
- $resourceGroupsLocksReadOnlyCount = ($htResourceLocks.Keys.where({ $htResourceLocks.($_).ResourceGroupsLocksReadOnlyCount -gt 0 } )).Count
-
- $resourcesLocksCannotDeleteCount = ($htResourceLocks.Keys.where( { $htResourceLocks.($_).ResourcesLocksCannotDeleteCount -gt 0 } )).Count
- $resourcesLocksReadOnlyCount = ($htResourceLocks.Keys.where( { $htResourceLocks.($_).ResourcesLocksReadOnlyCount -gt 0 } )).Count
-
- [void]$htmlTenantSummary.AppendLine(@"
-
Resource Locks
-
-
Considerations before applying locks docs
-
Note: Detailed information on Resource Locks is provided in the *_ResourceLocks.csv
-
-
-
-Lock scope
-Lock type
-presence
-
-
-
-Subscription CannotDelete $($subscriptionLocksCannotDeleteCount) of $totalSubCount Subscriptions
-Subscription ReadOnly $($subscriptionLocksReadOnlyCount) of $totalSubCount Subscriptions
-ResourceGroup CannotDelete $($resourceGroupsLocksCannotDeleteCount) of $totalSubCount Subscriptions (total: $(($htResourceLocks.Values.ResourceGroupsLocksCannotDeleteCount | Measure-Object -Sum).Sum))
-ResourceGroup ReadOnly $($resourceGroupsLocksReadOnlyCount) of $totalSubCount Subscriptions (total: $(($htResourceLocks.Values.ResourceGroupsLocksReadOnlyCount | Measure-Object -Sum).Sum))
-Resource CannotDelete $($resourcesLocksCannotDeleteCount) of $totalSubCount Subscriptions (total: $(($htResourceLocks.Values.ResourcesLocksCannotDeleteCount | Measure-Object -Sum).Sum))
-Resource ReadOnly $($resourcesLocksReadOnlyCount) of $totalSubCount Subscriptions (total: $(($htResourceLocks.Values.ResourcesLocksReadOnlyCount | Measure-Object -Sum).Sum))
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No Resource Locks at all docs
-'@)
- }
- $endResourceLocks = Get-Date
- Write-Host " ResourceLocks processing duration: $((New-TimeSpan -Start $startResourceLocks -End $endResourceLocks).TotalMinutes) minutes ($((New-TimeSpan -Start $startResourceLocks -End $endResourceLocks).TotalSeconds) seconds)"
- #endregion SUMMARYSubResourceLocks
-
- #SUMMARYSubDefenderPlansSubscriptionsSkipped
- if ($arrayDefenderPlansSubscriptionsSkipped.Count -gt 0) {
- #region SUMMARYSubDefenderPlansSubscriptionsSkipped
- Write-Host ' processing TenantSummary Subscriptions Microsoft Defender for Cloud plans SubscriptionsSkipped'
-
- $tfCount = $defenderPlansGroupedByPlanCount
- $startDefenderPlans = Get-Date
-
- $htmlTableId = 'TenantSummary_DefenderPlansSubscriptionsSkipped'
-
- [void]$htmlTenantSummary.AppendLine(@"
-
Microsoft Defender for Cloud plans - Subscriptions skipped
-
-
Register Resource Provider 'Microsoft.Security' docs
-
Microsoft Defender for Cloud's enhanced security features docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription Name
-Subscription Id
-Subscription QuotaId
-Subscription MG path
-reason
-
-
-
-"@)
-
- foreach ($subscription in $arrayDefenderPlansSubscriptionsSkipped | Sort-Object -Property subscriptionName) {
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($subscription.subscriptionName)
- $($subscription.subscriptionId)
- $($subscription.subscriptionQuotaId)
- $($subscription.subscriptionMgPath)
- $($subscription.reason)
-
-"@)
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
-
- $endDefenderPlans = Get-Date
- Write-Host " Microsoft Defender for Cloud plans SubscriptionsSkipped processing duration: $((New-TimeSpan -Start $startDefenderPlans -End $endDefenderPlans).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefenderPlans -End $endDefenderPlans).TotalSeconds) seconds)"
- #endregion SUMMARYSubDefenderPlansSubscriptionsSkipped
- }
-
- #region SUMMARYSubDefenderPlansByPlan
- Write-Host ' processing TenantSummary Subscriptions Microsoft Defender for Cloud plans by plan'
-
- $tfCount = $defenderPlansGroupedByPlanCount
- $startDefenderPlans = Get-Date
-
- if ($defenderPlansGroupedByPlanCount -gt 0) {
- $htmlTableId = 'TenantSummary_DefenderPlans'
-
- [void]$htmlTenantSummary.AppendLine(@"
-
Microsoft Defender for Cloud plans (by plan)
-
-"@)
-
- if ($defenderPlanDeprecatedContainerRegistry) {
- [void]$htmlTenantSummary.AppendLine(@'
-
Using deprecated plan 'Container registries' docs
-'@)
- }
- if ($defenderPlanDeprecatedKubernetesService) {
- [void]$htmlTenantSummary.AppendLine(@'
-
Using deprecated plan 'Kubernetes' docs
-'@)
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
Microsoft Defender for Cloud's enhanced security features docs
-
Download CSV
semicolon |
comma
-
-
-
-Plan/Tier
-Subscription Count
-
-
-
-"@)
-
- foreach ($defenderCapabilityAndTier in $defenderPlansGroupedByPlan | Sort-Object -Property Name) {
- if ($defenderCapabilityAndTier.Name -eq 'ContainerRegistry, Standard' -or $defenderCapabilityAndTier.Name -eq 'KubernetesService, Standard') {
- $thisDefenderPlan = " $($defenderCapabilityAndTier.Name)"
- }
- else {
- $thisDefenderPlan = $defenderCapabilityAndTier.Name
- }
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($thisDefenderPlan)
- $($defenderCapabilityAndTier.Count)
-
-"@)
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No Microsoft Defender for Cloud plans at all
-'@)
- }
- $endDefenderPlans = Get-Date
- Write-Host " Microsoft Defender for Cloud plans by plan processing duration: $((New-TimeSpan -Start $startDefenderPlans -End $endDefenderPlans).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefenderPlans -End $endDefenderPlans).TotalSeconds) seconds)"
- #endregion SUMMARYSubDefenderPlansByPlan
-
- #region SUMMARYSubDefenderPlansBySubscription
- Write-Host ' processing TenantSummary Subscriptions Microsoft Defender for Cloud plans by Subscription'
- $tfCount = $subsDefenderPlansCount
- $startDefenderPlans = Get-Date
-
- if (($arrayDefenderPlans).Count -gt 0) {
- $htmlTableId = 'TenantSummary_DefenderPlansBySubscription'
-
- [void]$htmlTenantSummary.AppendLine(@"
-
Microsoft Defender for Cloud plans (by Subscription)
-
-"@)
-
- if ($defenderPlanDeprecatedContainerRegistry) {
- [void]$htmlTenantSummary.AppendLine(@'
-
Using deprecated plan 'Container registries' docs
-'@)
- }
- if ($defenderPlanDeprecatedKubernetesService) {
- [void]$htmlTenantSummary.AppendLine(@'
-
Using deprecated plan 'Kubernetes' docs
-'@)
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
Microsoft Defender for Cloud's enhanced security features docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-Subscription MG path
-"@)
-
- foreach ($defenderCapability in $defenderCapabilities) {
- if (($defenderPlanDeprecatedContainerRegistry -and $defenderCapability -eq 'ContainerRegistry') -or ($defenderPlanDeprecatedKubernetesService -and $defenderCapability -eq 'KubernetesService')) {
- $thisDefenderCapability = " $($defenderCapability)"
- }
- else {
- $thisDefenderCapability = $defenderCapability
- }
- [void]$htmlTenantSummary.AppendLine(@"
- $($thisDefenderCapability)
-"@)
-
- }
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-
-'@)
-
- foreach ($sub in $defenderPlansGroupedBySub) {
- $nameSplit = $sub.Name.split(', ')
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($nameSplit[0])
- $($nameSplit[1])
- $($nameSplit[2])
-
-"@)
-
- foreach ($plan in $sub.Group | Sort-Object -Property defenderPlan) {
- [void]$htmlTenantSummary.AppendLine(@"
- $($plan.defenderPlanTier)
-"@)
- }
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- }
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No Microsoft Defender for Cloud plans at all
-'@)
- }
- $endDefenderPlans = Get-Date
- Write-Host " Microsoft Defender for Cloud plans by Subscription processing duration: $((New-TimeSpan -Start $startDefenderPlans -End $endDefenderPlans).TotalMinutes) minutes ($((New-TimeSpan -Start $startDefenderPlans -End $endDefenderPlans).TotalSeconds) seconds)"
- #endregion SUMMARYSubDefenderPlansBySubscription
-
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- #region SUMMARYSubUserAssignedIdentities4Resources
- Write-Host ' processing TenantSummary Subscriptions UserAssigned Managed Identities assigned to Resources'
- $arrayUserAssignedIdentities4ResourcesCount = $arrayUserAssignedIdentities4Resources.Count
- $tfCount = $arrayUserAssignedIdentities4ResourcesCount
- $startUserAssignedIdentities4Resources = Get-Date
-
- if ($arrayUserAssignedIdentities4ResourcesCount -gt 0) {
-
- $script:htUserAssignedIdentitiesAssignedResources = @{}
- $script:htResourcesAssignedUserAssignedIdentities = @{}
- foreach ($entry in $arrayUserAssignedIdentities4Resources) {
- #UserAssignedIdentities
- if (-not $htUserAssignedIdentitiesAssignedResources.($entry.miPrincipalId)) {
- $script:htUserAssignedIdentitiesAssignedResources.($entry.miPrincipalId) = @{}
- $script:htUserAssignedIdentitiesAssignedResources.($entry.miPrincipalId).ResourcesCount = 1
- }
- else {
- $script:htUserAssignedIdentitiesAssignedResources.($entry.miPrincipalId).ResourcesCount++
- }
- #Resources
- if (-not $htResourcesAssignedUserAssignedIdentities.(($entry.resourceId).tolower())) {
- $script:htResourcesAssignedUserAssignedIdentities.(($entry.resourceId).tolower()) = @{}
- $script:htResourcesAssignedUserAssignedIdentities.(($entry.resourceId).tolower()).UserAssignedIdentitiesCount = 1
- }
- else {
- $script:htResourcesAssignedUserAssignedIdentities.(($entry.resourceId).tolower()).UserAssignedIdentitiesCount++
- }
- }
-
- $htmlTableId = 'TenantSummary_UserAssignedIdentities4Resources'
-
- [void]$htmlTenantSummary.AppendLine(@"
-
UserAssigned Managed Identities assigned to Resources / vice versa
-
-
Managed identity 'user-assigned' vs 'system-assigned' docs
-
Download CSV
semicolon |
comma
-
-
-
-MI Name
-MI MgPath
-MI Subscription Name
-MI Subscription Id
-MI ResourceGroup
-MI ResourceId
-MI AAD SP objectId
-MI AAD SP applicationId
-MI count Res assignments
-MI used cross subscription
-Res Name
-Res Type
-Res MgPath
-Res Subscription Name
-Res Subscription Id
-Res ResourceGroup
-Res Id
-Res count assigned MIs
-"@)
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-
-'@)
-
- $userAssignedIdentities4Resources4CSVExport = [System.Collections.ArrayList]@()
- foreach ($miResEntry in $arrayUserAssignedIdentities4Resources | Sort-Object -Property miResourceId, resourceId) {
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($miResEntry.miResourceName)
- $($miResEntry.miMgPath)
- $($miResEntry.miSubscriptionName)
- $($miResEntry.miSubscriptionId)
- $($miResEntry.miResourceGroupName)
- $($miResEntry.miResourceId)
- $($miResEntry.miPrincipalId)
- $($miResEntry.miClientId)
- $($htUserAssignedIdentitiesAssignedResources.($miResEntry.miPrincipalId).ResourcesCount)
- $($miResEntry.miCrossSubscription)
- $($miResEntry.resourceName)
- $($miResEntry.resourceType)
- $($miResEntry.resourceMgPath)
- $($miResEntry.resourceSubscriptionName)
- $($miResEntry.resourceSubscriptionId)
- $($miResEntry.resourceResourceGroupName)
- $($miResEntry.resourceId)
- $($htResourcesAssignedUserAssignedIdentities.(($miResEntry.resourceId).tolower()).UserAssignedIdentitiesCount)
-
-"@)
-
- if (-not $NoCsvExport) {
- $null = $userAssignedIdentities4Resources4CSVExport.Add([PSCustomObject]@{
- MIName = $miResEntry.miResourceName
- MIMgPath = $miResEntry.miMgPath
- MISubscriptionName = $miResEntry.miSubscriptionName
- MISubscriptionId = $miResEntry.miSubscriptionId
- MIResourceGroup = $miResEntry.miResourceGroupName
- MIResourceId = $miResEntry.miResourceId
- MIAADSPObjectId = $miResEntry.miPrincipalId
- MIAADSPApplicationId = $miResEntry.miClientId
- MICountResAssignments = $htUserAssignedIdentitiesAssignedResources.($miResEntry.miPrincipalId).ResourcesCount
- MICrossSubscription = $miResEntry.miCrossSubscription
- ResName = $miResEntry.resourceName
- ResType = $miResEntry.resourceType
- ResMgPath = $miResEntry.resourceMgPath
- ResSubscriptionName = $miResEntry.resourceSubscriptionName
- ResSubscriptionId = $miResEntry.resourceSubscriptionId
- ResResourceGroup = $miResEntry.resourceResourceGroupName
- ResId = $miResEntry.resourceId
- ResCountAssignedMIs = $htResourcesAssignedUserAssignedIdentities.(($miResEntry.resourceId).tolower()).UserAssignedIdentitiesCount
- })
- }
-
- }
-
- if (-not $NoCsvExport) {
- Write-Host " Exporting UserAssignedIdentities4Resources CSV '$($outputPath)$($DirectorySeparatorChar)$($fileName)_UserAssignedIdentities4Resources.csv'"
- $userAssignedIdentities4Resources4CSVExport | Sort-Object -Property MIResourceId, ResId | Export-Csv -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName)_UserAssignedIdentities4Resources.csv" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No UserAssigned Managed Identities assigned to Resources / vice versa - at all
-'@)
- }
- $endUserAssignedIdentities4Resources = Get-Date
- Write-Host " UserAssigned Managed Identities assigned to Resources processing duration: $((New-TimeSpan -Start $startUserAssignedIdentities4Resources -End $endUserAssignedIdentities4Resources).TotalMinutes) minutes ($((New-TimeSpan -Start $startUserAssignedIdentities4Resources -End $endUserAssignedIdentities4Resources).TotalSeconds) seconds)"
- #endregion SUMMARYSubUserAssignedIdentities4Resources
-
- #region SUMMARYPSRule
- if ($azAPICallConf['htParameters'].DoPSRule -eq $true) {
- $startPSRule = Get-Date
- Write-Host ' processing TenantSummary PSRule'
- $arrayPSRuleCount = $arrayPsRule.Count
-
- if ($arrayPSRuleCount -gt 0) {
-
- if (-not $NoCsvExport) {
- $PSRuleCSVPath = "$($outputPath)$($DirectorySeparatorChar)$($fileName)_PSRule.csv"
- Write-Host " Exporting 'PSRule for Azure' CSV '$PSRuleCSVPath'"
- $arrayPsRule | Sort-Object -Property resourceId, pillar, category, severity, rule, recommendation | Export-Csv -Path $PSRuleCSVPath -Delimiter "$csvDelimiter" -NoTypeInformation
-
- if ($azAPICallConf['htParameters'].onGitHubActions -eq $true) {
- $exportCSVPSRuleFileSize = (Get-Item -Path $PSRuleCSVPath).length / 1MB
- if ($exportCSVPSRuleFileSize -gt 100) {
- Write-Host " The exported 'PSRule for Azure' CSV '$PSRuleCSVPath' exceeds the GitHub file limit of 100MB"
- Write-Host ' more info: https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-large-files-on-github#file-size-limits'
- Write-Host ' ! ---> Hint: Consider using additional parameter -PSRuleFailedOnly / results will only include failed resources'
- Write-Host " Re-Exporting 'PSRule for Azure' CSV '$PSRuleCSVPath' excluding column 'description'"
- $arrayPsRule | Select-Object -ExcludeProperty description | Sort-Object -Property resourceId, pillar, category, severity, rule, recommendation | Export-Csv -Path "$PSRuleCSVPath" -Delimiter "$csvDelimiter" -NoTypeInformation
-
- $exportCSVPSRuleFileSize = (Get-Item -Path $PSRuleCSVPath).length / 1MB
- if ($exportCSVPSRuleFileSize -gt 100) {
- Write-Host " The exported 'PSRule for Azure' CSV '$PSRuleCSVPath' still exceeds the GitHub file limit of 100MB"
- Write-Host " Re-Exporting 'PSRule for Azure' CSV '$PSRuleCSVPath' excluding column 'description', 'recommendation'"
- $arrayPsRule | Select-Object -ExcludeProperty description, recommendation | Sort-Object -Property resourceId, pillar, category, severity, rule | Export-Csv -Path "$PSRuleCSVPath" -Delimiter "$csvDelimiter" -NoTypeInformation
- }
-
- $exportCSVPSRuleFileSize = (Get-Item -Path $PSRuleCSVPath).length / 1MB
- if ($exportCSVPSRuleFileSize -gt 100) {
- Write-Host " The exported 'PSRule for Azure' CSV '$PSRuleCSVPath' still exceeds the GitHub file limit of 100MB"
- Write-Host " Deleting 'PSRule for Azure' CSV '$PSRuleCSVPath' in order to prevent the workflow from failing at push to repo"
- Remove-Item -Path $PSRuleCSVPath
- }
- }
- else {
- Write-Host " Info: The exported 'PSRule for Azure' CSV '$PSRuleCSVPath' does not exceed the GitHub file limit of 100MB"
- }
- }
- }
-
- $grpPSRuleAll = $arrayPsRule | Group-Object -Property resourceType, pillar, category, severity, rule, result
- $tfCount = $grpPSRuleAll.Name.Count
-
- $htmlTableId = 'TenantSummary_PSRule'
-
- [void]$htmlTenantSummary.AppendLine(@"
-
$tfCount PSRule for Azure results
-
-
Learn about PSRule for Azure
-
Download CSV
semicolon |
comma
-
-
-
-Resource Type
-Resource Count
-Subscription Count
-Pillar
-Category
-Severity
-Rule
-Recommendation
-lnk
-State
-
-
-
-"@)
-
- foreach ($result in $grpPSRuleAll | Sort-Object -Property Name) {
- $resultNameSplit = $result.Name.split(', ')
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($resultNameSplit[0])
- $($result.Group.Count)
- $(($result.Group.subscriptionId | Sort-Object -Unique).Count)
- $($resultNameSplit[1])
- $($resultNameSplit[2])
- $($resultNameSplit[3])
- $(($result.Group[0].rule))
- $(($result.Group[0].recommendation))
-
- $($resultNameSplit[5])
-
-"@)
-
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No PSRule for Azure results
-'@)
- }
- $endPSRule = Get-Date
- Write-Host " PSRule for Azure processing duration: $((New-TimeSpan -Start $startPSRule -End $endPSRule).TotalMinutes) minutes ($((New-TimeSpan -Start $startPSRule -End $endPSRule).TotalSeconds) seconds)"
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
PSRule for Azure -
integration paused - PSRule for Azure
-'@)
- }
- #endregion SUMMARYPSRule
- }
-
- #region SUMMARYStorageAccountAnalysis
- if ($azAPICallConf['htParameters'].NoStorageAccountAccessAnalysis -eq $false) {
- $startStorageAccountAnalysis = Get-Date
- Write-Host ' processing TenantSummary Storage Account Access Analysis'
-
- $arrayStorageAccountAnalysisResultsCount = $arrayStorageAccountAnalysisResults.Count
- if ($arrayStorageAccountAnalysisResultsCount -gt 0) {
-
- if (-not $NoCsvExport) {
- $storageAccountAccessAnalysisCSVPath = "$($outputPath)$($DirectorySeparatorChar)$($fileName)_StorageAccountAccessAnalysis.csv"
- Write-Host " Exporting 'Storage Account Access Analysis' CSV '$storageAccountAccessAnalysisCSVPath'"
- $arrayStorageAccountAnalysisResults | Sort-Object -Property StorageAccount | Export-Csv -Path $storageAccountAccessAnalysisCSVPath -Delimiter "$csvDelimiter" -NoTypeInformation
- }
-
- $saAnonymousAccessCount = ($arrayStorageAccountAnalysisResults.where({ $_.containersAnonymousContainerCount -gt 0 -or $_.containersAnonymousBlobCount -gt 0 })).Count
- $saStaticWebsitesEnabledCount = ($arrayStorageAccountAnalysisResults.where({ $_.staticWebsitesState -eq $true })).Count
-
- $htmlTableId = 'TenantSummary_StorageAccountAccessAnalysis'
- $tfCount = $arrayStorageAccountAnalysisResultsCount
-
- if ($DoAzureConsumption -eq $true) {
- $costDays = " ($($AzureConsumptionPeriod)d)"
- }
- else {
- $costDays = " (-DoAzureConsumption = $DoAzureConsumption )"
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
- $tfCount Storage Accounts Access Analysis results - Anonymous Access Container/Blob: $saAnonymousAccessCount, Static Website enabled: $saStaticWebsitesEnabledCount
-
-
Check this article by Elli Shlomo (MVP) Azure Blob Container Threats & Attacks
-
If you enabled the parameters StorageAccountAccessAnalysisSubscriptionTags or StorageAccountAccessAnalysisStorageAccountTags these are integrated in the CSV output *_StorageAccountAccessAnalysis.csv
-
Download CSV
semicolon |
comma
-
-
-
-StorageAccount
-Kind
-SkuName
-SkuTier
-Location
-Subscription
-Subscription MGPath
-ResourceGroup
-Allow Blob Public Access
-Public Network Access
-NetworkAcls defaultAction
-StaticWebsites State
-StaticWebsites Response
-Containers CanBeListed
-Containers Count
-Containers Anonymous Container Count
-Containers Anonymous Blob Count
-IpRules Count
-IpRules IPAddress List
-VirtualNetwork Rules Count
-ResourceAccess Rules Count
-ResourceAccess Rules
-Bypass
-Supports Https Traffic Only
-Minimum Tls Version
-Allow SharedKey Access
-Require Infrastructure Encryption
-Allowed Copy Scope
-Allow Cross Tenant Replication
-DNS Endpoint Type
-Used Capacity (GB)
-Cost$costDays
-Currency
-Cost categories
-
-
-
-"@)
-
- foreach ($result in $arrayStorageAccountAnalysisResults | Sort-Object -Property storageAccount) {
-
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($result.storageAccount)
- $($result.kind)
- $($result.skuName)
- $($result.skuTier)
- $($result.location)
- $($result.SubscriptionName)
- $($result.subscriptionMGPath)
- $($result.resourceGroup)
- $($result.allowBlobPublicAccess)
- $($result.publicNetworkAccess)
- $($result.networkAclsdefaultAction)
- $($result.staticWebsitesState)
- $($result.staticWebsitesResponse)
- $($result.containersCanBeListed)
- $($result.containersCount)
- $($result.containersAnonymousContainerCount)
- $($result.containersAnonymousBlobCount)
- $($result.ipRulesCount)
- $($result.ipRulesIPAddressList)
- $($result.virtualNetworkRulesCount)
- $($result.resourceAccessRulesCount)
- $($result.resourceAccessRules)
- $($result.bypass)
- $($result.supportsHttpsTrafficOnly)
- $($result.minimumTlsVersion)
- $($result.allowSharedKeyAccess)
- $($result.requireInfrastructureEncryption)
- $($result.allowedCopyScope)
- $($result.allowCrossTenantReplication)
- $($result.dnsEndpointType)
- $($result.usedCapacity)
- $($result.cost)
- $($result.curreny)
- $($result.metercategory)
-
-"@)
-
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
- No Storage Accounts found
-'@)
- }
- $endStorageAccountAnalysis = Get-Date
- Write-Host " Storage Account Analysis processing duration: $((New-TimeSpan -Start $startStorageAccountAnalysis -End $endStorageAccountAnalysis).TotalMinutes) minutes ($((New-TimeSpan -Start $startStorageAccountAnalysis -End $endStorageAccountAnalysis).TotalSeconds) seconds)"
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
- Storage Account Access Analysis disabled - parameter -NoStorageAccountAccessAnalysis = $($azAPICallConf['htParameters'].NoStorageAccountAccessAnalysis)
-"@)
- }
- #endregion SUMMARYStorageAccountAnalysis
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- #endregion tenantSummarySubscriptionsResourceDefenderPSRule
-
- #region tenantSummaryNetwork
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-'@)
-
- #region SUMMARYVNets
- if ($azAPICallConf['htParameters'].NoNetwork -eq $false) {
- $startVNets = Get-Date
- Write-Host ' processing TenantSummary VNets'
- $Vnets = $arrayVirtualNetworks | Sort-Object -Property SubscriptionName, VNet, VNetId -Unique | Select-Object SubscriptionName, Subscription, MGPath, VNet, VNetResourceGroup, Location, AddressSpaceAddressPrefixes, DhcpoptionsDnsservers, SubnetsCount, SubnetsWithNSGCount, SubnetsWithRouteTableCount, SubnetsWithDelegationsCount, PrivateEndpointsCount, SubnetsWithPrivateEndPointsCount, ConnectedDevices, SubnetsWithConnectedDevicesCount, DdosProtection, PeeringsCount
- $VNetsCount = $Vnets.Count
-
- if (-not $NoCsvExport) {
- $virtualNetworksCSVPath = "$($outputPath)$($DirectorySeparatorChar)$($fileName)_VirtualNetworks.csv"
- Write-Host " Exporting VirtaulNetworks CSV '$virtualNetworksCSVPath'"
- $Vnets | Export-Csv -Path $virtualNetworksCSVPath -Delimiter "$csvDelimiter" -NoTypeInformation
- }
-
- if ($VNetsCount -gt 0) {
-
- $htmlTableId = 'TenantSummary_VNets'
- $tfCount = $VNetsCount
- [void]$htmlTenantSummary.AppendLine(@"
-
$tfCount Virtual Networks
-
-
Download CSV
semicolon |
comma
-
-
-
-Subscription Name
-Subscription
-MGPath
-VNet
-VNet Resource Group
-Location
-Address Prefixes
-DNS Servers
-Subnets
-Subnets with NSG
-Subnets with RouteTable
-Subnets with Delegation
-Private Endpoints
-Subnets with Private Endpoints
-Connected device
-Subnets with connected device
-DDoS
-Peerings Count
-
-
-
-"@)
-
- foreach ($result in $Vnets) {
-
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($result.SubscriptionName)
- $($result.Subscription)
- $($result.MGPath)
- $($result.VNet)
- $($result.VNetResourceGroup)
- $($result.Location)
- $($result.AddressSpaceAddressPrefixes)
- $($result.DhcpoptionsDnsservers)
- $($result.SubnetsCount)
- $($result.SubnetsWithNSGCount)
- $($result.SubnetsWithRouteTableCount)
- $($result.SubnetsWithDelegationsCount)
- $($result.PrivateEndpointsCount)
- $($result.SubnetsWithPrivateEndPointsCount)
- $($result.ConnectedDevices)
- $($result.SubnetsWithConnectedDevicesCount)
- $($result.DdosProtection)
- $($result.PeeringsCount)
-
-"@)
-
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No Virtual Networks
-'@)
- }
- $endVNets = Get-Date
- Write-Host " VNets processing duration: $((New-TimeSpan -Start $startVNets -End $endVNets).TotalMinutes) minutes ($((New-TimeSpan -Start $startVNets -End $endVNets).TotalSeconds) seconds)"
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Virtual Networks - Network Analysis disabled - parameter -NoNetwork = $($azAPICallConf['htParameters'].NoNetwork)
-"@)
- }
- #endregion SUMMARYVNets
-
- #region SUMMARYSubnets
- if ($azAPICallConf['htParameters'].NoNetwork -eq $false) {
- $startSubnets = Get-Date
- Write-Host ' processing TenantSummary Subnets'
- $subnets = $arraySubnets | Sort-Object -Property SubscriptionName, VNet, VNetId, SubnetName
- $subnetsCount = $subnets.Count
-
- if (-not $NoCsvExport) {
- $subnetsCSVPath = "$($outputPath)$($DirectorySeparatorChar)$($fileName)_VirtualNetworkSubnets.csv"
- Write-Host " Exporting Subnets CSV '$subnetsCSVPath'"
- $subnets | Export-Csv -Path $subnetsCSVPath -Delimiter "$csvDelimiter" -NoTypeInformation
- }
-
- if ($subnetsCount -gt 0) {
-
- $subnetIPAddressUsageCriticalCount = ($subnets.where({ $_.SubnetIPAddressUsageCritical -eq $true })).Count
- $criticalUsageText = ''
- if ($subnetIPAddressUsageCriticalCount -gt 0) {
- $criticalUsageText = " ($subnetIPAddressUsageCriticalCount > $($NetworkSubnetIPAddressUsageCriticalPercentage)% IP addresses used)"
- }
-
- $htmlTableId = 'TenantSummary_Subnets'
- $tfCount = $subnetsCount
- [void]$htmlTenantSummary.AppendLine(@"
-
$tfCount Subnets$($criticalUsageText)
-
-
Download CSV
semicolon |
comma
-
-
-
-Subscription Name
-Subscription
-MGPath
-VNet
-VNet Resource Group
-Location
-Name
-Id
-Subnet
-Prefix
-Mask
-Range
-Connected devices
-Free IP addresses
-Used IP addresses %
-Private Endpoint Network Policies
-Private Link Service Network Policies
-Service Endpoints count
-Service Endpoints
-Delegation
-NSG
-Route Table
-Nat Gateway
-Private Endpoints
-
-
-
-"@)
-
- foreach ($result in $subnets) {
-
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($result.SubscriptionName)
- $($result.Subscription)
- $($result.MGPath)
- $($result.VNet)
- $($result.VNetResourceGroup)
- $($result.Location)
- $($result.SubnetName)
- $($result.SubnetId)
- $($result.SubnetNet)
- $($result.SubnetPrefix)
- $($result.Subnetmask)
- $($result.Range)
- $($result.ConnectedDevices)
- $($result.AvailableIPAddresses)
- $($result.UsedIPAddressesPercent)
- $($result.PrivateEndpointNetworkPolicies)
- $($result.PrivateLinkServiceNetworkPolicies)
- $($result.ServiceEndpointsCount)
- $($result.ServiceEndpoints)
- $($result.Delegation)
- $($result.NetworkSecurityGroup)
- $($result.RouteTable)
- $($result.NatGateway)
- $($result.PrivateEndpoints)
-
-"@)
-
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No Subnets
-'@)
- }
- $endSubnets = Get-Date
- Write-Host " Subnets processing duration: $((New-TimeSpan -Start $startSubnets -End $endSubnets).TotalMinutes) minutes ($((New-TimeSpan -Start $startSubnets -End $endSubnets).TotalSeconds) seconds)"
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Subnets - Network Analysis disabled - parameter -NoNetwork = $($azAPICallConf['htParameters'].NoNetwork)
-"@)
- }
- #endregion SUMMARYSubnets
-
- #region SUMMARYVNetPeerings
- if ($azAPICallConf['htParameters'].NoNetwork -eq $false) {
- $startVNetPeerings = Get-Date
- Write-Host ' processing TenantSummary VNet Peerings'
- $vnetPeerings = $arrayVirtualNetworks.where({ $_.PeeringsCount -gt 0 }) | Sort-Object -Property SubscriptionName, VNet, VNetId
- $VNetsPeeringsCount = $vnetPeerings.Count
-
- if (-not $NoCsvExport) {
- $virtualNetworkPeeringsCSVPath = "$($outputPath)$($DirectorySeparatorChar)$($fileName)_VirtualNetworkPeerings.csv"
- Write-Host " Exporting VirtaulNetworks CSV '$virtualNetworkPeeringsCSVPath'"
- $vnetPeerings | Export-Csv -Path $virtualNetworkPeeringsCSVPath -Delimiter "$csvDelimiter" -NoTypeInformation
- }
-
- if ($VNetsPeeringsCount -gt 0) {
- $vnetPeeringsGroupedByPeeringState = $vnetPeerings | Group-Object -Property PeeringState
- $arrayPeeringState = foreach ($peeringState in $vnetPeeringsGroupedByPeeringState) {
- "$($peeringState.Name): $($peeringState.Count)"
- }
-
- $xTenantPeeringsCount = $vnetPeerings.where({ $_.PeeringXTenant -eq 'true' }).Count
-
- $htmlTableId = 'TenantSummary_VNetPeerings'
- $tfCount = $VNetsPeeringsCount
- [void]$htmlTenantSummary.AppendLine(@"
-
$VNetsPeeringsCount Virtual Network Peerings - ($($arrayPeeringState -join "$CSVDelimiterOpposite ")) (Cross Tenant: $($xTenantPeeringsCount))
-
-
Download CSV
semicolon |
comma
-
-
-
-Subscription Name
-Subscription
-MGPath
-VNet
-VNet Resource Group
-Location
-Address Prefixes
-DNS Servers
-Subnets
-Subnets with NSG
-Subnets with RouteTable
-Subnets with Delegation
-Private Endpoints
-Subnets with Private Endpoints
-Connected device
-Subnets with connected device
-DDoS
-Peerings Count
-Peering Cross Tenant
-Peering Name
-Peering State
-Peering Sync Level
-Allow Virtual Network Access
-Allow Forwarded Traffic
-Allow Gateway Transit
-Use Remote Gateways
-Do Not Verify Remote Gateways
-Peer Complete Vnets
-Route Service Vips
-
-Remote Peerings Count
-Remote Peering Name
-Remote Peering State
-Remote Peering Sync Level
-Remote Allow Virtual Network Access
-Remote Allow Forwarded Traffic
-Remote Allow Gateway Transit
-Remote Use Remote Gateways
-Remote Do Not Verify Remote Gateways
-Remote Peer Complete Vnets
-Remote Route Service Vips
-
-Remote Subscription Name
-Remote Subscription
-Remote MGPath
-Remote VNet
-Remote VNet State
-Remote VNet Resource Group
-Remote Location
-Remote Address Space Address Prefixes
-Remote Virtual Network AddressSpace Address Prefixes
-
-Remote DNS Servers
-Remote Subnets
-Remote Subnets with NSG
-Remote Subnets with RouteTable
-Remote Subnets with Delegation
-Remote Private Endpoints
-Remote Subnets with Private Endpoints
-Remote Connected devices
-Remote Subnets with connected devices
-Remote DDoS
-
-
-
-
-"@)
-
- foreach ($result in $vnetPeerings) {
-
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($result.SubscriptionName)
- $($result.Subscription)
- $($result.MGPath)
- $($result.VNet)
- $($result.VNetResourceGroup)
- $($result.Location)
- $($result.AddressSpaceAddressPrefixes)
- $($result.DhcpoptionsDnsservers)
- $($result.SubnetsCount)
- $($result.SubnetsWithNSGCount)
- $($result.SubnetsWithRouteTableCount)
- $($result.SubnetsWithDelegationsCount)
- $($result.PrivateEndpointsCount)
- $($result.SubnetsWithPrivateEndPointsCount)
- $($result.ConnectedDevices)
- $($result.SubnetsWithConnectedDevicesCount)
- $($result.DdosProtection)
- $($result.PeeringsCount)
- $($result.PeeringXTenant)
- $($result.PeeringName)
- $($result.PeeringState)
- $($result.PeeringSyncLevel)
- $($result.AllowVirtualNetworkAccess)
- $($result.AllowForwardedTraffic)
- $($result.AllowGatewayTransit)
- $($result.UseRemoteGateways)
- $($result.DoNotVerifyRemoteGateways)
- $($result.PeerCompleteVnets)
- $($result.RouteServiceVips)
-
- $($result.RemotePeeringsCount)
- $($result.RemotePeeringName)
- $($result.RemotePeeringState)
- $($result.RemotePeeringSyncLevel)
- $($result.RemoteAllowVirtualNetworkAccess)
- $($result.RemoteAllowForwardedTraffic)
- $($result.RemoteAllowGatewayTransit)
- $($result.RemoteUseRemoteGateways)
- $($result.RemoteDoNotVerifyRemoteGateways)
- $($result.RemotePeerCompleteVnets)
- $($result.RemoteRouteServiceVips)
-
- $($result.RemoteSubscriptionName)
- $($result.RemoteSubscription)
- $($result.RemoteMGPath)
- $($result.RemoteVNet)
- $($result.RemoteVNetState)
- $($result.RemoteVNetResourceGroup)
- $($result.RemoteVNetLocation)
- $($result.RemoteAddressSpaceAddressPrefixes)
- $($result.RemoteVirtualNetworkAddressSpaceAddressPrefixes)
- $($result.RemoteDhcpoptionsDnsservers)
- $($result.RemoteSubnetsCount)
- $($result.RemoteSubnetsWithNSGCount)
- $($result.RemoteSubnetsWithRouteTable)
- $($result.RemoteSubnetsWithDelegations)
- $($result.RemotePrivateEndPoints)
- $($result.RemoteSubnetsWithPrivateEndPoints)
- $($result.RemoteConnectedDevices)
- $($result.RemoteSubnetsWithConnectedDevices)
- $($result.RemoteDdosProtection)
-
-"@)
-
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No Virtual Network Peerings
-'@)
- }
- $endVNetPeerings = Get-Date
- Write-Host " VNet Peerings processing duration: $((New-TimeSpan -Start $startVNetPeerings -End $endVNetPeerings).TotalMinutes) minutes ($((New-TimeSpan -Start $startVNetPeerings -End $endVNetPeerings).TotalSeconds) seconds)"
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Virtual Network Peerings - Network Analysis disabled - parameter -NoNetwork = $($azAPICallConf['htParameters'].NoNetwork)
-"@)
- }
- #endregion SUMMARYVNetPeerings
-
- #region SUMMARYPrivateEndpoints
- if ($azAPICallConf['htParameters'].NoNetwork -eq $false) {
- $startPrivateEndpoints = Get-Date
- Write-Host ' processing TenantSummary PrivateEndpoints'
- $privateEndPoints = $arrayPrivateEndpointsEnriched | Sort-Object -Property PESubscriptionName, PEName
- $privateEndPointsCount = $privateEndPoints.Count
-
- if (-not $NoCsvExport) {
- $peCSVPath = "$($outputPath)$($DirectorySeparatorChar)$($fileName)_PrivateEndpoints.csv"
- Write-Host " Exporting PrivateEndpoints CSV '$peCSVPath'"
- $privateEndPoints | Export-Csv -Path $peCSVPath -Delimiter "$csvDelimiter" -NoTypeInformation
- }
-
- if ($privateEndPointsCount -gt 0) {
-
- $crossSubPECount = ($privateEndPoints.where({ $_.crossSubscriptionPE -eq $true })).Count
- $crossSubPEText = ''
- if ($crossSubPECount -gt 0) {
- $crossSubPEText = " ($crossSubPECount cross Subscription)"
- }
- $crossTenantPECount = ($privateEndPoints.where({ $_.crossTenantPE -eq $true })).Count
- $crossTenantPEText = ''
- if ($crossTenantPECount -gt 0) {
- $crossTenantPEText = " ($crossTenantPECount cross Tenant)"
- }
-
- $htmlTableId = 'TenantSummary_PrivateEndpoints'
- $tfCount = $privateEndPointsCount
- [void]$htmlTenantSummary.AppendLine(@"
-
$tfCount Private Endpoints$($crossSubPEText)$($crossTenantPEText)
-
-
Download CSV
semicolon |
comma
-
-
-
-
-PE Name
-PE Id
-PE Location
-PE Resource Group
-PE Subscription Name
-PE Subscription
-PE MGPath
-PE Type
-PE State
-Cross Subscription PE
-Cross Tenant PE
-
-Resource
-Resource Type
-Resource Id
-Target Subresource
-NIC Name
-FQDN
-IP addresses
-Resource Resource Group
-Resource Subscription Name
-Resource Subscription Id
-Resource MGPath
-Resource Cross Tenant
-
-Subnet
-Subnet Id
-VNet
-VNet Id
-VNet Location
-VNet Resource Group
-Subnet Subscription Name
-Subnet Subscription Id
-Subnet MGPath
-
-
-
-"@)
-
- foreach ($result in $privateEndPoints) {
-
- [void]$htmlTenantSummary.AppendLine(@"
-
- $($result.PEName)
- $($result.PEId)
- $($result.PELocation)
- $($result.PEResourceGroup)
- $($result.PESubscriptionName)
- $($result.PESubscription)
- $($result.PEMGPath)
- $($result.PEConnectionType)
- $($result.PEConnectionState)
- $($result.CrossSubscriptionPE)
- $($result.CrossTenantPE)
-
- $($result.Resource)
- $($result.ResourceType)
- $($result.ResourceId)
- $($result.TargetSubresource)
- $($result.NICName)
- $($result.FQDN)
- $($result.ipAddresses)
- $($result.ResourceResourceGroup)
- $($result.ResourceSubscriptionName)
- $($result.ResourceSubscriptionId)
- $($result.ResourceMGPath)
- $($result.ResourceCrossTenant)
-
- $($result.Subnet)
- $($result.SubnetId)
- $($result.SubnetVNet)
- $($result.SubnetVNetId)
- $($result.SubnetVNetLocation)
- $($result.SubnetVNetResourceGroup)
- $($result.SubnetSubscriptionName)
- $($result.SubnetSubscription)
- $($result.SubnetMGPath)
-
-"@)
-
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No Private Endpoints
-'@)
- }
- $endPrivateEndpoints = Get-Date
- Write-Host " PrivateEndpoints processing duration: $((New-TimeSpan -Start $startPrivateEndpoints -End $endPrivateEndpoints).TotalMinutes) minutes ($((New-TimeSpan -Start $startPrivateEndpoints -End $endPrivateEndpoints).TotalSeconds) seconds)"
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Private Endpoints - Network Analysis disabled - parameter -NoNetwork = $($azAPICallConf['htParameters'].NoNetwork)
-"@)
- }
- #endregion SUMMARYPrivateEndpoints
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- #endregion tenantSummaryNetwork
-
- showMemoryUsage
-
- #region tenantSummaryDiagnostics
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-'@)
-
- [void]$htmlTenantSummary.AppendLine( @'
-
Management Groups
-'@)
-
- #region SUMMARYDiagnosticsManagementGroups
- Write-Host ' processing TenantSummary Diagnostics Management Groups'
-
- #hasDiag
- if ($diagnosticSettingsMgCount -gt 0) {
- $tfCount = $diagnosticSettingsMgCount
- $htmlTableId = 'TenantSummary_DiagnosticsManagementGroups'
- [void]$htmlTenantSummary.AppendLine(@"
-
$diagnosticSettingsMgManagementGroupsCount ($mgsDiagnosticsApplicableCount) Management Groups configured for Diagnostic settings ($diagnosticSettingsMgCount settings)
-
-
Management Group Diagnostic Settings - Create Or Update - REST API docs
-
Download CSV
semicolon |
comma
-
-
-
-Management Group Name
-Management Group Id
-Diagnostic setting
-Inheritance
-Inherited from
-Target
-TargetId
-"@)
-
- foreach ($logCategory in $diagnosticSettingsMgCategories) {
- [void]$htmlTenantSummary.AppendLine(@"
-$logCategory
-"@)
- }
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-
-'@)
- $htmlSUMMARYDiagnosticsManagementGroups = $null
- $htmlSUMMARYDiagnosticsManagementGroups = foreach ($entry in $diagnosticSettingsMg | Sort-Object -Property ScopeMgPath, DiagnosticsInheritedFrom, DiagnosticSettingName, DiagnosticTargetType, DiagnosticTargetId) {
-
- @"
-
-$($entry.ScopeName -replace '<', '<' -replace '>', '>')
-$($entry.ScopeId)
-$($entry.DiagnosticSettingName)
-$($entry.DiagnosticsInheritedOrnot)
-$($entry.DiagnosticsInheritedFrom)
-$($entry.DiagnosticTargetType)
-$($entry.DiagnosticTargetId)
-"@
- foreach ($logCategory in $diagnosticSettingsMgCategories) {
- if ($entry.DiagnosticCategoriesHt.($logCategory)) {
- @"
- $($entry.DiagnosticCategoriesHt.($logCategory))
-"@
- }
- else {
- @'
- n/a
-'@
- }
- }
- @'
-
-'@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYDiagnosticsManagementGroups)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
-
- [void]$htmlTenantSummary.AppendLine(@'
-
No Management Groups configured for Diagnostic settings docs
-'@)
- }
-
- #hasNoDiag
- if ($arrayMgsWithoutDiagnosticsCount -gt 0) {
- $tfCount = $arrayMgsWithoutDiagnosticsCount
- $htmlTableId = 'TenantSummary_NoDiagnosticsManagementGroups'
- [void]$htmlTenantSummary.AppendLine(@"
-
$arrayMgsWithoutDiagnosticsCount Management Groups NOT configured for Diagnostic settings docs
-
-
Management Group Diagnostic Settings - Create Or Update - REST API docs
-
Download CSV
semicolon |
comma
-
-
-
-Management Group Name
-Management Group Id
-Management Group path
-
-
-
-"@)
- $htmlSUMMARYNoDiagnosticsManagementGroups = $null
- $htmlSUMMARYNoDiagnosticsManagementGroups = foreach ($entry in $arrayMgsWithoutDiagnostics | Sort-Object -Property ScopeMgPath) {
-
- @"
-
-$($entry.ScopeName -replace '<', '<' -replace '>', '>')
-$($entry.ScopeId)
-$($entry.ScopeMgPath)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYNoDiagnosticsManagementGroups)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
-
- [void]$htmlTenantSummary.AppendLine(@'
-
All Management Groups are configured for Diagnostic settings docs
-'@)
- }
- #endregion SUMMARYDiagnosticsManagementGroups
-
- #region subscriptions
- [void]$htmlTenantSummary.AppendLine( @'
-
Subscriptions
-'@)
-
- #region SUMMARYDiagnosticsSubscriptions
- Write-Host ' processing TenantSummary Diagnostics Subscriptions'
-
- #hasDiag
- if ($diagnosticSettingsSubCount -gt 0) {
- $tfCount = $diagnosticSettingsSubCount
- $htmlTableId = 'TenantSummary_DiagnosticsSubscriptions'
- [void]$htmlTenantSummary.AppendLine(@"
-
$diagnosticSettingsSubSubscriptionsCount Subscriptions configured for Diagnostic settings ($diagnosticSettingsSubCount settings)
-
-
Create diagnostic setting docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-Path
-Diagnostic setting
-Target
-TargetId
-"@)
-
- foreach ($logCategory in $diagnosticSettingsSubCategories) {
- [void]$htmlTenantSummary.AppendLine(@"
-$logCategory
-"@)
- }
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-
-'@)
- $htmlSUMMARYDiagnosticsSubscriptions = $null
- $htmlSUMMARYDiagnosticsSubscriptions = foreach ($entry in $diagnosticSettingsSub | Sort-Object -Property ScopeName, DiagnosticTargetType, DiagnosticSettingName) {
-
- @"
-
-$($entry.ScopeName -replace '<', '<' -replace '>', '>')
-$($entry.ScopeId)
- $($entry.ScopeMgPath)
-$($entry.DiagnosticSettingName)
-$($entry.DiagnosticTargetType)
-$($entry.DiagnosticTargetId)
-"@
- foreach ($logCategory in $diagnosticSettingsSubCategories) {
- if ($entry.DiagnosticCategoriesHt.($logCategory)) {
- @"
- $($entry.DiagnosticCategoriesHt.($logCategory))
-"@
- }
- else {
- @'
- n/a
-'@
- }
- }
- @'
-
-'@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYDiagnosticsSubscriptions)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
-
- [void]$htmlTenantSummary.AppendLine(@'
-
No Subscriptions configured for Diagnostic settings docs
-'@)
- }
-
- #hasNoDiag
- if ($diagnosticSettingsSubNoDiagCount -gt 0) {
- $tfCount = $diagnosticSettingsSubNoDiagCount
- $htmlTableId = 'TenantSummary_NoDiagnosticsSubscriptions'
- [void]$htmlTenantSummary.AppendLine(@"
-
$diagnosticSettingsSubNoDiagCount Subscriptions NOT configured for Diagnostic settings
-
-
Create diagnostic setting docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-Subscription Id
-Subscription Mg path
-
-
-
-"@)
- $htmlSUMMARYNoDiagnosticsSubscriptions = $null
- $htmlSUMMARYNoDiagnosticsSubscriptions = foreach ($entry in $diagnosticSettingsSubNoDiag | Sort-Object -Property ScopeMgPath) {
-
- @"
-
-$($entry.ScopeName -replace '<', '<' -replace '>', '>')
-$($entry.ScopeId)
-$($entry.ScopeMgPath)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYNoDiagnosticsSubscriptions)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
-
- [void]$htmlTenantSummary.AppendLine(@'
-
All Subscriptions are configured for Diagnostic settings docs
-'@)
- }
- #endregion SUMMARYDiagnosticsSubscriptions
-
- #endregion subscriptions
-
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- #region resources
- [void]$htmlTenantSummary.AppendLine( @'
-
Resources
-'@)
-
- #region SUMMARYResourcesDiagnosticsCapable
- Write-Host ' processing TenantSummary Diagnostics Resources Diagnostics Capable (1st party only)'
- $resourceTypesDiagnosticsArraySorted = $resourceTypesDiagnosticsArray | Sort-Object -Property ResourceType, ResourceCount, Metrics, Logs, LogCategories
- $resourceTypesDiagnosticsArraySortedCount = ($resourceTypesDiagnosticsArraySorted | Measure-Object).count
- $resourceTypesDiagnosticsMetricsTrueCount = ($resourceTypesDiagnosticsArray.where( { $_.Metrics -eq $True }) | Measure-Object).count
- $resourceTypesDiagnosticsLogsTrueCount = ($resourceTypesDiagnosticsArray.where( { $_.Logs -eq $True }) | Measure-Object).count
- $resourceTypesDiagnosticsMetricsLogsTrueCount = ($resourceTypesDiagnosticsArray.where( { $_.Metrics -eq $True -or $_.Logs -eq $True }) | Measure-Object).count
- if ($resourceTypesDiagnosticsArraySortedCount -gt 0) {
- $tfCount = $resourceTypesDiagnosticsArraySortedCount
- $htmlTableId = 'TenantSummary_ResourcesDiagnosticsCapable'
- [void]$htmlTenantSummary.AppendLine(@"
-
Resources (1st party) Diagnostics capable $resourceTypesDiagnosticsMetricsLogsTrueCount/$resourceTypesDiagnosticsArraySortedCount ResourceTypes ($resourceTypesDiagnosticsMetricsTrueCount Metrics, $resourceTypesDiagnosticsLogsTrueCount Logs)
-
-
Create Custom Policies for Azure ResourceTypes that support Diagnostics Logs and Metrics Create-AzDiagPolicy
-
Supported categories for Azure Resource Logs docs
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Resource Count
-Diagnostics capable
-Metrics
-Logs
-LogCategories
-
-
-
-"@)
- $htmlSUMMARYResourcesDiagnosticsCapable = $null
- $htmlSUMMARYResourcesDiagnosticsCapable = foreach ($resourceType in $resourceTypesDiagnosticsArraySorted) {
- if ($resourceType.Metrics -eq $true -or $resourceType.Logs -eq $true) {
- $diagnosticsCapable = $true
- }
- else {
- if ($resourceType.Metrics -eq 'n/a - resourcesMeanwhileDeleted' -or $resourceType.Logs -eq 'n/a - resourcesMeanwhileDeleted') {
- $diagnosticsCapable = 'n/a'
- }
- else {
- $diagnosticsCapable = $false
- }
- }
- @"
-
-$($resourceType.ResourceType)
-$($resourceType.ResourceCount)
-$diagnosticsCapable
-$($resourceType.Metrics)
-$($resourceType.Logs)
-$($resourceType.LogCategories -join "$CsvDelimiterOpposite ")
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYResourcesDiagnosticsCapable)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
-
- [void]$htmlTenantSummary.AppendLine(@'
-
No Resources (1st party) Diagnostics capable
-'@)
- }
- #endregion SUMMARYResourcesDiagnosticsCapable
-
- #region SUMMARYDiagnosticsPolicyLifecycle
- if (-not $NoResourceDiagnosticsPolicyLifecycle) {
- Write-Host ' processing TenantSummary Diagnostics Resource Diagnostics Policy Lifecycle'
- $startsumDiagLifecycle = Get-Date
-
- if ($tenantCustomPoliciesCount -gt 0) {
- $policiesThatDefineDiagnostics = $tenantCustomPolicies.where( { $_.Type -eq 'custom' -and $_.Json.properties.policyrule.then.details.type -eq 'Microsoft.Insights/diagnosticSettings' -and $_.Json.properties.policyrule.then.details.deployment.properties.template.resources.type -match '/providers/diagnosticSettings' } )
-
- $policiesThatDefineDiagnosticsCount = ($policiesThatDefineDiagnostics).count
- if ($policiesThatDefineDiagnosticsCount -gt 0) {
-
- $diagnosticsPolicyAnalysis = @()
- $diagnosticsPolicyAnalysis = [System.Collections.ArrayList]@()
- foreach ($policy in $policiesThatDefineDiagnostics) {
-
- if (
- (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).properties.workspaceId -or
- (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).properties.eventHubAuthorizationRuleId -or
- (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).properties.storageAccountId
- ) {
- if ( (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).properties.workspaceId) {
- $diagnosticsDestination = 'LA'
- }
- if ( (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).properties.eventHubAuthorizationRuleId) {
- $diagnosticsDestination = 'EH'
- }
- if ( (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).properties.storageAccountId) {
- $diagnosticsDestination = 'SA'
- }
-
- if ( (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).properties.logs ) {
-
- $resourceType = ( (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).type -replace '/providers/diagnosticSettings')
-
- $resourceTypeCountFromResourceTypesSummarizedArray = ($resourceTypesSummarizedArray.where( { $_.ResourceType -eq $resourceType })).ResourceCount
- if ($resourceTypeCountFromResourceTypesSummarizedArray) {
- $resourceCount = $resourceTypeCountFromResourceTypesSummarizedArray
- }
- else {
- $resourceCount = '0'
- }
- $supportedLogs = $resourceTypesDiagnosticsArray | Where-Object { $_.ResourceType -eq ( (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).type -replace '/providers/diagnosticSettings') }
-
- $diagnosticsLogCategoriesSupported = $supportedLogs.LogCategories
- if (($supportedLogs | Measure-Object).count -gt 0) {
- $logsSupported = 'yes'
- }
- else {
- $logsSupported = 'no'
- }
-
- $roleDefinitionIdsArray = [System.Collections.ArrayList]@()
- foreach ($roleDefinitionId in ($policy).Json.properties.policyrule.then.details.roleDefinitionIds) {
- if (($htCacheDefinitionsRole).($roleDefinitionId -replace '.*/')) {
- $null = $roleDefinitionIdsArray.Add("
$(($htCacheDefinitionsRole).($roleDefinitionId -replace '.*/').Name) ($($roleDefinitionId -replace '.*/'))")
- }
- else {
- Write-Host " DiagnosticsLifeCycle: unknown RoleDefinition '$roleDefinitionId'"
- $null = $roleDefinitionIdsArray.Add("unknown RoleDefinition: '$roleDefinitionId'")
- }
- }
-
- $policyHasPolicyAssignments = $policyBaseQuery | Where-Object { $_.PolicyDefinitionId -eq $policy.Id } | Sort-Object -Property PolicyDefinitionId, PolicyAssignmentId -Unique
- $policyHasPolicyAssignmentCount = ($policyHasPolicyAssignments | Measure-Object).count
- if ($policyHasPolicyAssignmentCount -gt 0) {
- $policyAssignmentsArray = @()
- $policyAssignmentsArray += foreach ($policyAssignment in $policyHasPolicyAssignments) {
- "$($policyAssignment.PolicyAssignmentId) (
$($policyAssignment.PolicyAssignmentDisplayName) )"
- }
- $policyAssignmentsCollCount = ($policyAssignmentsArray).count
- $policyAssignmentsColl = $policyAssignmentsCollCount
- }
- else {
- $policyAssignmentsColl = 0
- }
-
- #PolicyUsedinPolicySet
- $policySetAssignmentsColl = 0
- $policySetAssignmentsArray = @()
- $policyUsedinPolicySets = 'n/a'
-
- $usedInPolicySetArray = [System.Collections.ArrayList]@()
- foreach ($customPolicySet in $tenantCustomPolicySets) {
- if ($customPolicySet.Type -eq 'Custom') {
- $hlpCustomPolicySet = ($customPolicySet)
- if (($hlpCustomPolicySet.PolicySetPolicyIds) -contains ($policy.Id)) {
- $null = $usedInPolicySetArray.Add("$($hlpCustomPolicySet.Id) (
$($hlpCustomPolicySet.DisplayName) )")
-
- #PolicySetHasAssignments
- $policySetAssignments = ($htCacheAssignmentsPolicy).Values.where( { $_.Assignment.properties.policyDefinitionId -eq ($hlpCustomPolicySet.Id) } )
- $policySetAssignmentsCount = ($policySetAssignments).count
- if ($policySetAssignmentsCount -gt 0) {
- $policySetAssignmentsArray += foreach ($policySetAssignment in $policySetAssignments) {
- "$(($policySetAssignment.Assignment.id).Tolower()) (
$($policySetAssignment.Assignment.properties.displayName) )"
- }
- $policySetAssignmentsCollCount = ($policySetAssignmentsArray).Count
- $policySetAssignmentsColl = "$policySetAssignmentsCollCount [$($policySetAssignmentsArray -join "$CsvDelimiterOpposite ")]"
- }
-
- }
- }
- }
-
- if (($usedInPolicySetArray | Measure-Object).count -gt 0) {
- $policyUsedinPolicySets = "$(($usedInPolicySetArray | Measure-Object).count) [$($usedInPolicySetArray -join "$CsvDelimiterOpposite ")]"
- }
- else {
- $policyUsedinPolicySets = "$(($usedInPolicySetArray | Measure-Object).count)"
- }
-
- if ($recommendation -eq 'review the policy and add the missing categories as required') {
- if ($policyAssignmentsColl -gt 0 -or $policySetAssignmentsColl -gt 0) {
- $priority = '1-High'
- }
- else {
- $priority = '3-MediumLow'
- }
- }
- else {
- $priority = '4-Low'
- }
-
- $diagnosticsLogCategoriesCoveredByPolicy = (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).properties.logs
- if (($diagnosticsLogCategoriesCoveredByPolicy.category | Measure-Object).count -gt 0) {
-
- if (($supportedLogs | Measure-Object).count -gt 0) {
- $actionItems = @()
- $actionItems += foreach ($supportedLogCategory in $supportedLogs.LogCategories) {
- if ($diagnosticsLogCategoriesCoveredByPolicy.category -notcontains ($supportedLogCategory)) {
- $supportedLogCategory
- }
- }
- if (($actionItems | Measure-Object).count -gt 0) {
- $diagnosticsLogCategoriesNotCoveredByPolicy = $actionItems
- $recommendation = 'review the policy and add the missing categories as required'
- }
- else {
- $diagnosticsLogCategoriesNotCoveredByPolicy = 'all OK'
- $recommendation = 'no recommendation'
- }
- }
- else {
- $status = 'Azure Governance Visualizer did not detect the resourceType'
- $diagnosticsLogCategoriesSupported = 'n/a'
- $diagnosticsLogCategoriesNotCoveredByPolicy = 'n/a'
- $recommendation = 'no recommendation as this resourceType seems not existing'
- $logsSupported = 'unknown'
- }
-
- $null = $diagnosticsPolicyAnalysis.Add([PSCustomObject]@{
- Priority = $priority
- PolicyId = ($policy).Id
- PolicyCategory = ($policy).Category
- PolicyName = ($policy).DisplayName
- PolicyDeploysRoles = $roleDefinitionIdsArray -join "$CsvDelimiterOpposite "
- PolicyForResourceTypeExists = $true
- ResourceType = $resourceType
- ResourceTypeCount = $resourceCount
- Status = $status
- LogsSupported = $logsSupported
- LogCategoriesInPolicy = ($diagnosticsLogCategoriesCoveredByPolicy.category | Sort-Object) -join "$CsvDelimiterOpposite "
- LogCategoriesSupported = ($diagnosticsLogCategoriesSupported | Sort-Object) -join "$CsvDelimiterOpposite "
- LogCategoriesDelta = ($diagnosticsLogCategoriesNotCoveredByPolicy | Sort-Object) -join "$CsvDelimiterOpposite "
- Recommendation = $recommendation
- DiagnosticsTargetType = $diagnosticsDestination
- PolicyAssignments = $policyAssignmentsColl
- PolicyUsedInPolicySet = $policyUsedinPolicySets
- PolicySetAssignments = $policySetAssignmentsColl
- })
-
- }
- else {
- $status = 'no categories defined'
- $priority = '5-Low'
- $recommendation = 'Review the policy - the definition has key for categories, but there are none categories defined'
- $null = $diagnosticsPolicyAnalysis.Add([PSCustomObject]@{
- Priority = $priority
- PolicyId = ($policy).Id
- PolicyCategory = ($policy).Category
- PolicyName = ($policy).DisplayName
- PolicyDeploysRoles = $roleDefinitionIdsArray -join "$CsvDelimiterOpposite "
- PolicyForResourceTypeExists = $true
- ResourceType = $resourceType
- ResourceTypeCount = $resourceCount
- Status = $status
- LogsSupported = $logsSupported
- LogCategoriesInPolicy = 'none'
- LogCategoriesSupported = ($diagnosticsLogCategoriesSupported | Sort-Object) -join "$CsvDelimiterOpposite "
- LogCategoriesDelta = ($diagnosticsLogCategoriesSupported | Sort-Object) -join "$CsvDelimiterOpposite "
- Recommendation = $recommendation
- DiagnosticsTargetType = $diagnosticsDestination
- PolicyAssignments = $policyAssignmentsColl
- PolicyUsedInPolicySet = $policyUsedinPolicySets
- PolicySetAssignments = $policySetAssignmentsColl
- })
- }
- }
- else {
- if (-not (($policy).Json.properties.policyrule.then.details.deployment.properties.template.resources | Where-Object { $_.type -match '/providers/diagnosticSettings' }).properties.metrics ) {
- Write-Host " DiagnosticsLifeCycle check?!: $($policy.DisplayName) ($($policy.Id)) - something unexpected, no Logs and no Metrics defined"
- }
- }
- }
- else {
- Write-Host " DiagnosticsLifeCycle check?!: $($policy.DisplayName) ($($policy.Id)) - something unexpected - not EH, LA, SA"
- }
- }
- #where no Policy exists
- $diagnosticsPolicyAnalysisCount = ($diagnosticsPolicyAnalysis).count
- if ($diagnosticsPolicyAnalysisCount -gt 0) {
- foreach ($resourceTypeDiagnosticsCapable in $resourceTypesDiagnosticsArray | Where-Object { $_.Logs -eq $true }) {
- if (($diagnosticsPolicyAnalysis.ResourceType).ToLower() -notcontains ( ($resourceTypeDiagnosticsCapable.ResourceType).ToLower() )) {
- $supportedLogs = ($resourceTypesDiagnosticsArray | Where-Object { $_.ResourceType -eq $resourceTypeDiagnosticsCapable.ResourceType }).LogCategories
- $logsSupported = 'yes'
- $resourceTypeCountFromResourceTypesSummarizedArray = ($resourceTypesSummarizedArray | Where-Object { $_.ResourceType -eq $resourceTypeDiagnosticsCapable.ResourceType }).ResourceCount
- if ($resourceTypeCountFromResourceTypesSummarizedArray) {
- $resourceCount = $resourceTypeCountFromResourceTypesSummarizedArray
- }
- else {
- $resourceCount = '0'
- }
- $recommendation = "Create diagnostics policy for this ResourceType. To verify GA check
docs "
- $null = $diagnosticsPolicyAnalysis.Add([PSCustomObject]@{
- Priority = '2-Medium'
- PolicyId = 'n/a'
- PolicyCategory = 'n/a'
- PolicyName = 'n/a'
- PolicyDeploysRoles = 'n/a'
- ResourceType = $resourceTypeDiagnosticsCapable.ResourceType
- ResourceTypeCount = $resourceCount
- Status = 'n/a'
- LogsSupported = $logsSupported
- LogCategoriesInPolicy = 'n/a'
- LogCategoriesSupported = $supportedLogs -join "$CsvDelimiterOpposite "
- LogCategoriesDelta = 'n/a'
- Recommendation = $recommendation
- DiagnosticsTargetType = 'n/a'
- PolicyForResourceTypeExists = $false
- PolicyAssignments = 'n/a'
- PolicyUsedInPolicySet = 'n/a'
- PolicySetAssignments = 'n/a'
- })
- }
- }
- }
-
- $diagnosticsPolicyAnalysisCount = ($diagnosticsPolicyAnalysis).count
-
- if ($diagnosticsPolicyAnalysisCount -gt 0) {
- $tfCount = $diagnosticsPolicyAnalysisCount
-
- $htmlTableId = 'TenantSummary_DiagnosticsLifecycle'
- [void]$htmlTenantSummary.AppendLine(@"
-
ResourceDiagnostics for Logs - Policy Lifecycle recommendations
-
-
Create Custom Policies for Azure ResourceTypes that support Diagnostics Logs and Metrics Create-AzDiagPolicy
-
Supported categories for Azure Resource Logs docs
-
-
-
-Priority
-Recommendation
-ResourceType
-Resource Count
-Diagnostics capable (logs)
-Policy Id
-Policy DisplayName
-Role definitions
-Target
-Log Categories not covered by Policy
-Policy assignments
-Policy used in PolicySet
-PolicySet assignments
-
-
-
-"@)
-
- foreach ($diagnosticsFinding in $diagnosticsPolicyAnalysis | Sort-Object -Property @{Expression = { $_.Priority } }, @{Expression = { $_.Recommendation } }, @{Expression = { $_.ResourceType } }, @{Expression = { $_.PolicyName } }, @{Expression = { $_.PolicyId } }) {
- [void]$htmlTenantSummary.AppendLine(@"
-
-
- $($diagnosticsFinding.Priority)
-
-
- $($diagnosticsFinding.Recommendation)
-
-
- $($diagnosticsFinding.ResourceType)
-
-
- $($diagnosticsFinding.ResourceTypeCount)
-
-
- $($diagnosticsFinding.LogsSupported)
-
-
- $($diagnosticsFinding.PolicyId)
-
-
- $($diagnosticsFinding.PolicyName)
-
-
- $($diagnosticsFinding.PolicyDeploysRoles)
-
-
- $($diagnosticsFinding.DiagnosticsTargetType)
-
-
- $($diagnosticsFinding.LogCategoriesDelta)
-
-
- $($diagnosticsFinding.PolicyAssignments)
-
-
- $($diagnosticsFinding.PolicyUsedInPolicySet)
-
-
- $($diagnosticsFinding.PolicySetAssignments)
-
-
-"@)
- }
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No ResourceDiagnostics Policy Lifecycle recommendations
-'@)
- }
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No ResourceDiagnostics Policy Lifecycle recommendations
-'@)
- }
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No ResourceDiagnostics Policy Lifecycle recommendations
-'@)
- }
- $endsumDiagLifecycle = Get-Date
- Write-Host " Resource Diagnostics Policy Lifecycle processing duration: $((New-TimeSpan -Start $startsumDiagLifecycle -End $endsumDiagLifecycle).TotalSeconds) seconds"
- }
- #endregion SUMMARYDiagnosticsPolicyLifecycle
-
- #endregion resources
- }
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
-
- #endregion tenantSummaryDiagnostics
-
- showMemoryUsage
-
- #region tenantSummaryLimits
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-"@)
-
- #region tenantSummaryLimitsTenant
- [void]$htmlTenantSummary.AppendLine( @'
-
Tenant
-'@)
-
- #policySets
- if ($tenantCustompolicySetsCount -gt (($LimitPOLICYPolicySetDefinitionsScopedTenant * $LimitCriticalPercentage) / 100)) {
- [void]$htmlTenantSummary.AppendLine(@"
-
PolicySet definitions: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant docs
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
PolicySet definitions: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant docs
-"@)
- }
-
- #CustomRoleDefinitions
- if ($tenantCustomRolesCount -gt (($LimitRBACCustomRoleDefinitionsTenant * $LimitCriticalPercentage) / 100)) {
- [void]$htmlTenantSummary.AppendLine(@"
-
Custom Role definitions: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant docs
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Custom Role definitions: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant docs
-"@)
- }
-
- #endregion tenantSummaryLimitsTenant
-
- #region tenantSummaryLimitsManagementGroups
- [void]$htmlTenantSummary.AppendLine( @'
-
Management Groups
-'@)
-
- #region SUMMARYMgsapproachingLimitsPolicyAssignments
- Write-Host ' processing TenantSummary ManagementGroups Limit PolicyAssignments'
- $mgsApproachingLimitPolicyAssignments = (($policyBaseQueryManagementGroups.where( { [String]::IsNullOrEmpty($_.SubscriptionId) -and $_.PolicyAndPolicySetAssignmentAtScopeCount -gt 0 -and (($_.PolicyAndPolicySetAssignmentAtScopeCount -gt ($LimitPOLICYPolicyAssignmentsManagementGroup * ($LimitCriticalPercentage / 100)))) })) | Select-Object MgId, MgName, PolicyAssignmentAtScopeCount, PolicySetAssignmentAtScopeCount, PolicyAndPolicySetAssignmentAtScopeCount, PolicyAssignmentLimit -Unique)
- if (($mgsApproachingLimitPolicyAssignments | Measure-Object).count -gt 0) {
- $tfCount = ($mgsApproachingLimitPolicyAssignments | Measure-Object).count
- $htmlTableId = 'TenantSummary_MgsapproachingLimitsPolicyAssignments'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($mgsApproachingLimitPolicyAssignments | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyAssignmentsManagementGroup) for PolicyAssignment
-
-
Azure Policy Limits docs
-
Download CSV
semicolon |
comma
-
-
-
-Management Group Name
-Management Group Id
-Limit
-
-
-
-"@)
- $htmlSUMMARYMgsapproachingLimitsPolicyAssignments = $null
- $htmlSUMMARYMgsapproachingLimitsPolicyAssignments = foreach ($mgApproachingLimitPolicyAssignments in $mgsApproachingLimitPolicyAssignments) {
- @"
-
-$($mgApproachingLimitPolicyAssignments.MgName -replace '<', '<' -replace '>', '>')
-$($mgApproachingLimitPolicyAssignments.MgId)
-$(($mgApproachingLimitPolicyAssignments.PolicyAndPolicySetAssignmentAtScopeCount/$LimitPOLICYPolicyAssignmentsManagementGroup).tostring('P')) ($($mgApproachingLimitPolicyAssignments.PolicyAndPolicySetAssignmentAtScopeCount)/$($LimitPOLICYPolicyAssignmentsManagementGroup)) ($($mgApproachingLimitPolicyAssignments.PolicyAssignmentAtScopeCount) Policy assignments, $($mgApproachingLimitPolicyAssignments.PolicySetAssignmentAtScopeCount) PolicySet assignments)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYMgsapproachingLimitsPolicyAssignments)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($mgsApproachingLimitPolicyAssignments | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyAssignmentsManagementGroup) for PolicyAssignment docs
-"@)
- }
- #endregion SUMMARYMgsapproachingLimitsPolicyAssignments
-
- #region SUMMARYMgsapproachingLimitsPolicyScope
- Write-Host ' processing TenantSummary ManagementGroups Limit PolicyScope'
- $mgsApproachingLimitPolicyScope = (($policyBaseQueryManagementGroups.where( { [String]::IsNullOrEmpty($_.SubscriptionId) -and $_.PolicyDefinitionsScopedCount -gt 0 -and (($_.PolicyDefinitionsScopedCount -gt ($LimitPOLICYPolicyDefinitionsScopedManagementGroup * ($LimitCriticalPercentage / 100)))) })) | Select-Object MgId, MgName, PolicyDefinitionsScopedCount, PolicyDefinitionsScopedLimit -Unique)
- if (($mgsApproachingLimitPolicyScope | Measure-Object).count -gt 0) {
- $tfCount = ($mgsApproachingLimitPolicyScope | Measure-Object).count
- $htmlTableId = 'TenantSummary_MgsapproachingLimitsPolicyScope'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($mgsApproachingLimitPolicyScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyDefinitionsScopedManagementGroup) for Policy Scope
-
-
Azure Policy Limits docs
-
Download CSV
semicolon |
comma
-
-
-
-Management Group Name
-Management Group Id
-Limit
-
-
-
-"@)
- $htmlSUMMARYMgsapproachingLimitsPolicyScope = $null
- $htmlSUMMARYMgsapproachingLimitsPolicyScope = foreach ($mgApproachingLimitPolicyScope in $mgsApproachingLimitPolicyScope) {
- @"
-
-$($mgApproachingLimitPolicyScope.MgName -replace '<', '<' -replace '>', '>')
-$($mgApproachingLimitPolicyScope.MgId)
-$(($mgApproachingLimitPolicyScope.PolicyDefinitionsScopedCount/$LimitPOLICYPolicyDefinitionsScopedManagementGroup).tostring('P')) $($mgApproachingLimitPolicyScope.PolicyDefinitionsScopedCount)/$($LimitPOLICYPolicyDefinitionsScopedManagementGroup)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYMgsapproachingLimitsPolicyScope)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($mgsApproachingLimitPolicyScope.count) Management Groups approaching Limit ($LimitPOLICYPolicyDefinitionsScopedManagementGroup) for Policy Scope docs
-"@)
- }
- #endregion SUMMARYMgsapproachingLimitsPolicyScope
-
- #region SUMMARYMgsapproachingLimitsPolicySetScope
- Write-Host ' processing TenantSummary ManagementGroups Limit PolicySetScope'
- $mgsApproachingLimitPolicySetScope = (($policyBaseQueryManagementGroups.where( { [String]::IsNullOrEmpty($_.SubscriptionId) -and $_.PolicySetDefinitionsScopedCount -gt 0 -and (($_.PolicySetDefinitionsScopedCount -gt ($LimitPOLICYPolicySetDefinitionsScopedManagementGroup * ($LimitCriticalPercentage / 100)))) })) | Select-Object MgId, MgName, PolicySetDefinitionsScopedCount, PolicySetDefinitionsScopedLimit -Unique)
- if ($mgsApproachingLimitPolicySetScope.count -gt 0) {
- $tfCount = ($mgsApproachingLimitPolicySetScope | Measure-Object).count
- $htmlTableId = 'TenantSummary_MgsapproachingLimitsPolicySetScope'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($mgsApproachingLimitPolicySetScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedManagementGroup) for PolicySet Scope
-
-
Azure Policy Limits docs
-
Download CSV
semicolon |
comma
-
-
-
-Management Group Name
-Management Group Id
-Limit
-
-
-
-"@)
- $htmlSUMMARYMgsapproachingLimitsPolicySetScope = $null
- $htmlSUMMARYMgsapproachingLimitsPolicySetScope = foreach ($mgApproachingLimitPolicySetScope in $mgsApproachingLimitPolicySetScope) {
- @"
-
-$($mgApproachingLimitPolicySetScope.MgName -replace '<', '<' -replace '>', '>')
-$($mgApproachingLimitPolicySetScope.MgId)
-$(($mgApproachingLimitPolicySetScope.PolicySetDefinitionsScopedCount/$LimitPOLICYPolicySetDefinitionsScopedManagementGroup).tostring('P')) ($($mgApproachingLimitPolicySetScope.PolicySetDefinitionsScopedCount)/$($LimitPOLICYPolicySetDefinitionsScopedManagementGroup))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYMgsapproachingLimitsPolicySetScope)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($mgsApproachingLimitPolicySetScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedManagementGroup) for PolicySet Scope docs
-"@)
- }
- #endregion SUMMARYMgsapproachingLimitsPolicySetScope
-
- #region SUMMARYMgsapproachingLimitsRoleAssignment
- Write-Host ' processing TenantSummary ManagementGroups Limit RoleAssignments'
- $mgsApproachingRoleAssignmentLimit = $rbacBaseQuery.where( { [String]::IsNullOrEmpty($_.SubscriptionId) -and $_.RoleAssignmentsCount -gt ($LimitRBACRoleAssignmentsManagementGroup * $LimitCriticalPercentage / 100) }) | Sort-Object -Property MgId -Unique | Select-Object -Property MgId, MgName, RoleAssignmentsCount, RoleAssignmentsLimit
-
- if (($mgsApproachingRoleAssignmentLimit).count -gt 0) {
- $tfCount = ($mgsApproachingRoleAssignmentLimit).count
- $htmlTableId = 'TenantSummary_MgsapproachingLimitsRoleAssignment'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($mgsApproachingRoleAssignmentLimit | Measure-Object).count) Management Groups approaching Limit ($LimitRBACRoleAssignmentsManagementGroup) for RoleAssignment
-
-
Azure RBAC Limits docs
-
Download CSV
semicolon |
comma
-
-
-
-Management Group Name
-Management Group Id
-Limit
-
-
-
-"@)
- $htmlSUMMARYMgsapproachingLimitsRoleAssignment = $null
- $htmlSUMMARYMgsapproachingLimitsRoleAssignment = foreach ($mgApproachingRoleAssignmentLimit in $mgsApproachingRoleAssignmentLimit) {
- @"
-
-$($mgApproachingRoleAssignmentLimit.MgName -replace '<', '<' -replace '>', '>')
-$($mgApproachingRoleAssignmentLimit.MgId)
-$(($mgApproachingRoleAssignmentLimit.RoleAssignmentsCount/$LimitRBACRoleAssignmentsManagementGroup).tostring('P')) ($($mgApproachingRoleAssignmentLimit.RoleAssignmentsCount)/$($LimitRBACRoleAssignmentsManagementGroup))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYMgsapproachingLimitsRoleAssignment)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($mgApproachingRoleAssignmentLimit | Measure-Object).count) Management Groups approaching Limit ($LimitRBACRoleAssignmentsManagementGroup) for RoleAssignment docs
-"@)
- }
- #endregion SUMMARYMgsapproachingLimitsRoleAssignment
-
- #endregion tenantSummaryLimitsManagementGroups
-
- #region tenantSummaryLimitsSubscriptions
- [void]$htmlTenantSummary.AppendLine( @'
-
Subscriptions
-'@)
-
- #region SUMMARYSubsapproachingLimitsResourceGroups
- Write-Host ' processing TenantSummary Subscriptions Limit Resource Groups'
- $subscriptionsApproachingLimitFromResourceGroupsAll = $resourceGroupsAll.where( { $_.count_ -gt ($LimitResourceGroups * ($LimitCriticalPercentage / 100)) })
- if (($subscriptionsApproachingLimitFromResourceGroupsAll | Measure-Object).count -gt 0) {
- $tfCount = ($subscriptionsApproachingLimitFromResourceGroupsAll | Measure-Object).count
- $htmlTableId = 'TenantSummary_SubsapproachingLimitsResourceGroups'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($subscriptionsApproachingLimitFromResourceGroupsAll | Measure-Object).count) Subscriptions approaching Limit ($LimitResourceGroups) for ResourceGroups
-
-
Azure Subscription Resource Group Limit docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-Limit
-
-
-
-"@)
- $htmlSUMMARYSubsapproachingLimitsResourceGroups = $null
- $htmlSUMMARYSubsapproachingLimitsResourceGroups = foreach ($subscriptionApproachingLimitFromResourceGroupsAll in $subscriptionsApproachingLimitFromResourceGroupsAll) {
- $subscriptionData = $htSubDetails.($subscriptionApproachingLimitFromResourceGroupsAll.subscriptionId).details
- @"
-
-$($subscriptionData.subscription -replace '<', '<' -replace '>', '>')
-$($subscriptionData.subscriptionId)
-$(($subscriptionApproachingLimitFromResourceGroupsAll.count_/$LimitResourceGroups).tostring('P')) ($($subscriptionApproachingLimitFromResourceGroupsAll.count_)/$($LimitResourceGroups))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSubsapproachingLimitsResourceGroups)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($subscriptionsApproachingLimitFromResourceGroupsAll | Measure-Object).count) Subscriptions approaching Limit ($LimitResourceGroups) for ResourceGroups docs
-"@)
- }
- #endregion SUMMARYSubsapproachingLimitsResourceGroups
-
- #region SUMMARYSubsapproachingLimitsSubscriptionTags
- Write-Host ' processing TenantSummary Subscriptions Limit Subscription Tags'
- $subscriptionsApproachingLimitTags = ($optimizedTableForPathQueryMgAndSub.where( { (($_.SubscriptionTagsCount -gt ($LimitTagsSubscription * ($LimitCriticalPercentage / 100)))) }))
- if (($subscriptionsApproachingLimitTags | Measure-Object).count -gt 0) {
- $tfCount = ($subscriptionsApproachingLimitTags | Measure-Object).count
- $htmlTableId = 'TenantSummary_SubsapproachingLimitsSubscriptionTags'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($subscriptionsApproachingLimitTags | Measure-Object).count) Subscriptions approaching Limit ($LimitTagsSubscription) for Tags
-
-
Azure Subscription Tag Limit docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-Limit
-
-
-
-"@)
- $htmlSUMMARYSubsapproachingLimitsSubscriptionTags = $null
- $htmlSUMMARYSubsapproachingLimitsSubscriptionTags = foreach ($subscriptionApproachingLimitTags in $subscriptionsApproachingLimitTags) {
- @"
-
-$($subscriptionApproachingLimitTags.subscription -replace '<', '<' -replace '>', '>')
-$($subscriptionApproachingLimitTags.subscriptionId)
-$(($subscriptionApproachingLimitTags.SubscriptionTagsCount/$LimitTagsSubscription).tostring('P')) ($($subscriptionApproachingLimitTags.SubscriptionTagsCount)/$($LimitTagsSubscription))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSubsapproachingLimitsSubscriptionTags)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($subscriptionsApproachingLimitTags.count) Subscriptions approaching Limit ($LimitTagsSubscription) for Tags docs
-"@)
- }
- #endregion SUMMARYSubsapproachingLimitsSubscriptionTags
-
- #region SUMMARYSubsapproachingLimitsPolicyAssignments
- Write-Host ' processing TenantSummary Subscriptions Limit PolicyAssignments'
- $subscriptionsApproachingLimitPolicyAssignments = (($policyBaseQuerySubscriptions.where( { -not [String]::IsNullOrEmpty($_.SubscriptionId) -and $_.PolicyAndPolicySetAssignmentAtScopeCount -gt 0 -and (($_.PolicyAndPolicySetAssignmentAtScopeCount -gt ($_.PolicyAssignmentLimit * ($LimitCriticalPercentage / 100)))) })) | Select-Object MgId, Subscription, SubscriptionId, PolicyAssignmentAtScopeCount, PolicySetAssignmentAtScopeCount, PolicyAndPolicySetAssignmentAtScopeCount, PolicyAssignmentLimit -Unique)
- if ($subscriptionsApproachingLimitPolicyAssignments.count -gt 0) {
- $tfCount = ($subscriptionsApproachingLimitPolicyAssignments | Measure-Object).count
- $htmlTableId = 'TenantSummary_SubsapproachingLimitsPolicyAssignments'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($subscriptionsApproachingLimitPolicyAssignments | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyAssignmentsSubscription) for PolicyAssignment
-
-
Azure Policy Limits docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-Limit
-
-
-
-"@)
- $htmlSUMMARYSubsapproachingLimitsPolicyAssignments = $null
- $htmlSUMMARYSubsapproachingLimitsPolicyAssignments = foreach ($subscriptionApproachingLimitPolicyAssignments in $subscriptionsApproachingLimitPolicyAssignments) {
- @"
-
-$($subscriptionApproachingLimitPolicyAssignments.subscription -replace '<', '<' -replace '>', '>')
-$($subscriptionApproachingLimitPolicyAssignments.subscriptionId)
-$(($subscriptionApproachingLimitPolicyAssignments.PolicyAndPolicySetAssignmentAtScopeCount/$subscriptionApproachingLimitPolicyAssignments.PolicyAssignmentLimit).tostring('P')) ($($subscriptionApproachingLimitPolicyAssignments.PolicyAndPolicySetAssignmentAtScopeCount)/$($subscriptionApproachingLimitPolicyAssignments.PolicyAssignmentLimit)) ($($subscriptionApproachingLimitPolicyAssignments.PolicyAssignmentAtScopeCount) Policy assignments, $($subscriptionApproachingLimitPolicyAssignments.PolicySetAssignmentAtScopeCount) PolicySet assignments)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSubsapproachingLimitsPolicyAssignments)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($subscriptionsApproachingLimitPolicyAssignments | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyAssignmentsSubscription) for PolicyAssignment docs
-"@)
- }
- #endregion SUMMARYSubsapproachingLimitsPolicyAssignments
-
- #region SUMMARYSubsapproachingLimitsPolicyScope
- Write-Host ' processing TenantSummary Subscriptions Limit PolicyScope'
- $subscriptionsApproachingLimitPolicyScope = (($policyBaseQuerySubscriptions.where( { -not [String]::IsNullOrEmpty($_.SubscriptionId) -and $_.PolicyDefinitionsScopedCount -gt 0 -and (($_.PolicyDefinitionsScopedCount -gt ($_.PolicyDefinitionsScopedLimit * ($LimitCriticalPercentage / 100)))) })) | Select-Object MgId, Subscription, SubscriptionId, PolicyDefinitionsScopedCount, PolicyDefinitionsScopedLimit -Unique)
- if (($subscriptionsApproachingLimitPolicyScope | Measure-Object).count -gt 0) {
- $tfCount = ($subscriptionsApproachingLimitPolicyScope | Measure-Object).count
- $htmlTableId = 'TenantSummary_SubsapproachingLimitsPolicyScope'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyDefinitionsScopedSubscription) for Policy Scope
-
-
Azure Policy Limits docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-Limit
-
-
-
-"@)
- $htmlSUMMARYSubsapproachingLimitsPolicyScope = $null
- $htmlSUMMARYSubsapproachingLimitsPolicyScope = foreach ($subscriptionApproachingLimitPolicyScope in $subscriptionsApproachingLimitPolicyScope) {
- @"
-
-$($subscriptionApproachingLimitPolicyScope.subscription -replace '<', '<' -replace '>', '>')
-$($subscriptionApproachingLimitPolicyScope.subscriptionId)
-$(($subscriptionApproachingLimitPolicyScope.PolicyDefinitionsScopedCount/$subscriptionApproachingLimitPolicyScope.PolicyDefinitionsScopedLimit).tostring('P')) ($($subscriptionApproachingLimitPolicyScope.PolicyDefinitionsScopedCount)/$($subscriptionApproachingLimitPolicyScope.PolicyDefinitionsScopedLimit))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSubsapproachingLimitsPolicyScope)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$($subscriptionsApproachingLimitPolicyScope.count) Subscriptions approaching Limit ($LimitPOLICYPolicyDefinitionsScopedSubscription) for Policy Scope docs
-"@)
- }
- #endregion SUMMARYSubsapproachingLimitsPolicyScope
-
- #region SUMMARYSubsapproachingLimitsPolicySetScope
- Write-Host ' processing TenantSummary Subscriptions Limit PolicySetScope'
- $subscriptionsApproachingLimitPolicySetScope = (($policyBaseQuerySubscriptions.where( { -not [String]::IsNullOrEmpty($_.SubscriptionId) -and $_.PolicySetDefinitionsScopedCount -gt 0 -and (($_.PolicySetDefinitionsScopedCount -gt ($_.PolicySetDefinitionsScopedLimit * ($LimitCriticalPercentage / 100)))) })) | Select-Object MgId, Subscription, SubscriptionId, PolicySetDefinitionsScopedCount, PolicySetDefinitionsScopedLimit -Unique)
- if ($subscriptionsApproachingLimitPolicySetScope.count -gt 0) {
- $tfCount = ($subscriptionsApproachingLimitPolicySetScope | Measure-Object).count
- $htmlTableId = 'TenantSummary_SubsapproachingLimitsPolicySetScope'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedSubscription) for PolicySet Scope
-
-
Azure Policy Limits docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-Limit
-
-
-
-"@)
- $htmlSUMMARYSubsapproachingLimitsPolicySetScope = $null
- $htmlSUMMARYSubsapproachingLimitsPolicySetScope = foreach ($subscriptionApproachingLimitPolicySetScope in $subscriptionsApproachingLimitPolicySetScope) {
- @"
-
-$($subscriptionApproachingLimitPolicySetScope.subscription -replace '<', '<' -replace '>', '>')
-$($subscriptionApproachingLimitPolicySetScope.subscriptionId)
-$(($subscriptionApproachingLimitPolicySetScope.PolicySetDefinitionsScopedCount/$subscriptionApproachingLimitPolicySetScope.PolicySetDefinitionsScopedLimit).tostring('P')) ($($subscriptionApproachingLimitPolicySetScope.PolicySetDefinitionsScopedCount)/$($subscriptionApproachingLimitPolicySetScope.PolicySetDefinitionsScopedLimit))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSubsapproachingLimitsPolicySetScope)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedSubscription) for PolicySet Scope docs
-"@)
- }
- #endregion SUMMARYSubsapproachingLimitsPolicySetScope
-
- #region SUMMARYSubsapproachingLimitsRoleAssignment
- Write-Host ' processing TenantSummary Subscriptions Limit RoleAssignments'
- $subscriptionsApproachingRoleAssignmentLimit = $rbacBaseQuery.where( { -not [String]::IsNullOrEmpty($_.SubscriptionId) -and $_.RoleAssignmentsCount -gt ($_.RoleAssignmentsLimit * $LimitCriticalPercentage / 100) }) | Sort-Object -Property SubscriptionId -Unique | Select-Object -Property MgId, SubscriptionId, Subscription, RoleAssignmentsCount, RoleAssignmentsLimit
-
- $availableSubscriptionsRoleAssignmentLimits = ($htSubscriptionsRoleAssignmentLimit.values | Sort-Object -Unique) -join ' | '
-
- if (($subscriptionsApproachingRoleAssignmentLimit).count -gt 0) {
- $tfCount = ($subscriptionsApproachingRoleAssignmentLimit).count
- $htmlTableId = 'TenantSummary_SubsapproachingLimitsRoleAssignment'
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($subscriptionsApproachingRoleAssignmentLimit | Measure-Object).count) Subscriptions approaching Limit ($($availableSubscriptionsRoleAssignmentLimits)) for RoleAssignment
-
-
Azure RBAC Limits docs
-
Download CSV
semicolon |
comma
-
-
-
-Subscription
-SubscriptionId
-Limit
-
-
-
-"@)
- $htmlSUMMARYSubsapproachingLimitsRoleAssignment = $null
- $htmlSUMMARYSubsapproachingLimitsRoleAssignment = foreach ($subscriptionApproachingRoleAssignmentLimit in $subscriptionsApproachingRoleAssignmentLimit) {
- @"
-
-$($subscriptionApproachingRoleAssignmentLimit.subscription -replace '<', '<' -replace '>', '>')
-$($subscriptionApproachingRoleAssignmentLimit.subscriptionId)
-$(($subscriptionApproachingRoleAssignmentLimit.RoleAssignmentsCount/$subscriptionApproachingRoleAssignmentLimit.RoleAssignmentsLimit).tostring('P')) ($($subscriptionApproachingRoleAssignmentLimit.RoleAssignmentsCount)/$($subscriptionApproachingRoleAssignmentLimit.RoleAssignmentsLimit))
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYSubsapproachingLimitsRoleAssignment)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$(($subscriptionsApproachingRoleAssignmentLimit | Measure-Object).count) Subscriptions approaching Limit ($availableSubscriptionsRoleAssignmentLimits) for RoleAssignment docs
-"@)
- }
- #endregion SUMMARYSubsapproachingLimitsRoleAssignment
-
- #endregion tenantSummaryLimitsSubscriptions
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- #endregion tenantSummaryLimits
-
- showMemoryUsage
-
- #region tenantSummaryAAD
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-
Check out AzADServicePrincipalInsights GitHub
-
Demystifying Service Principals - Managed Identities devBlogs
-
John Savill - Azure AD App Registrations, Enterprise Apps and Service Principals YouTube
-'@)
-
- #region AADSPNotFound
- Write-Host ' processing TenantSummary AAD ServicePrincipals - not found'
-
- if ($servicePrincipalRequestResourceNotFoundCount -gt 0) {
- $tfCount = $servicePrincipalRequestResourceNotFoundCount
- $htmlTableId = 'TenantSummary_AADSPNotFound'
-
- [void]$htmlTenantSummary.AppendLine(@"
-
$($servicePrincipalRequestResourceNotFoundCount) AAD ServicePrincipals 'Request_ResourceNotFound'
-
-
-
-
-Service Principal Object Id
-
-
-
-"@)
- $htmlSUMMARYAADSPNotFound = $null
- $htmlSUMMARYAADSPNotFound = foreach ($serviceprincipal in $arrayServicePrincipalRequestResourceNotFound | Sort-Object) {
-
- @"
-
-$($serviceprincipal)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYAADSPNotFound)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No ServicePrincipals where the API returned 'Request_ResourceNotFound'
-'@)
- }
- #endregion AADSPNotFound
-
- #region AADAppNotFound
- Write-Host ' processing TenantSummary AAD Applications - not found'
-
- if ($applicationRequestResourceNotFoundCount -gt 0) {
- $tfCount = $applicationRequestResourceNotFoundCount
- $htmlTableId = 'TenantSummary_AADAppNotFound'
-
- [void]$htmlTenantSummary.AppendLine(@"
-
$($applicationRequestResourceNotFoundCount) AAD Applications 'Request_ResourceNotFound'
-
-
-
-
-Application (Client) Id
-
-
-
-"@)
- $htmlSUMMARYAADAppNotFound = $null
- $htmlSUMMARYAADAppNotFound = foreach ($app in $arrayApplicationRequestResourceNotFound | Sort-Object) {
-
- @"
-
-$($app)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYAADAppNotFound)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No Applications where the API returned 'Request_ResourceNotFound'
-'@)
- }
- #endregion AADAppNotFound
-
- #region AADSPManagedIdentity
- $startAADSPManagedIdentityLoop = Get-Date
- Write-Host ' processing TenantSummary AAD SP Managed Identities'
-
- if ($servicePrincipalsOfTypeManagedIdentityCount -gt 0) {
- $tfCount = $servicePrincipalsOfTypeManagedIdentityCount
- $htmlTableId = 'TenantSummary_AADSPManagedIdentities'
-
- if ($htOrphanedSPMI.keys.Count -gt 0) {
- $orphanedSPMIPresent = $true
- }
-
- $abbr = "
"
- $abbrOrphanedSPMI = "
"
- [void]$htmlTenantSummary.AppendLine(@"
-
$($servicePrincipalsOfTypeManagedIdentityCount) AAD ServicePrincipals type=ManagedIdentity
-
-
Download CSV
semicolon |
comma
-
-
-
-ApplicationId
-DisplayName
-SP ObjectId
-Type
-Usage
-Usage info
-Policy assignment details
-Role assignments
-Assigned to resources$($abbr)
-Orphaned$($abbrOrphanedSPMI)
-
-
-
-"@)
- $htmlSUMMARYAADSPManagedIdentities = $null
- $htmlSUMMARYAADSPManagedIdentities = foreach ($serviceprincipalMI in $servicePrincipalsOfTypeManagedIdentity | Sort-Object) {
-
- $serviceprincipalMIDetailed = $htServicePrincipals.($serviceprincipalMI)
- $miRoleAssignments = 'n/a'
- $miType = 'unknown'
- $userMiAssignedToResourcesCount = ''
- foreach ($altName in $serviceprincipalMIDetailed.alternativeNames) {
- if ($altName -like 'isExplicit=*') {
- $splitAltName = $altName.split('=')
- if ($splitAltName[1] -eq 'true') {
- $miType = 'User assigned'
- if ($htUserAssignedIdentitiesAssignedResources.($serviceprincipalMI)) {
- $userMiAssignedToResourcesCount = $htUserAssignedIdentitiesAssignedResources.($serviceprincipalMI).ResourcesCount
- }
- }
- if ($splitAltName[1] -eq 'false') {
- $miType = 'System assigned'
- }
- }
- else {
- $s1 = $altName -replace '.*/providers/'; $rm = $s1 -replace '.*/'; $resourceType = $s1 -replace "/$($rm)"
- $miAlternativeName = $altname
- $miResourceType = $resourceType
- }
- }
-
- if ($miResourceType -eq 'Microsoft.Authorization/policyAssignments') {
- $policyAssignmentId = $miAlternativeName.ToLower()
- if ($policyAssignmentId -like '/providers/Microsoft.Management/managementGroups/*') {
- if (-not ($htCacheAssignmentsPolicy).($policyAssignmentId)) {
- $assignmentInfo = 'n/a'
- }
- else {
- $assignmentInfo = ($htCacheAssignmentsPolicy).($policyAssignmentId).Assignment
- }
- }
- else {
- #sub
- if (((($policyAssignmentId).Split('/') | Measure-Object).Count - 1) -eq 6) {
- if (-not ($htCacheAssignmentsPolicy).($policyAssignmentId)) {
- $assignmentInfo = 'n/a'
- }
- else {
- $assignmentInfo = ($htCacheAssignmentsPolicy).($policyAssignmentId).Assignment
- }
- }
- else {
- #rg
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- if (-not ($htCacheAssignmentsPolicyOnResourceGroupsAndResources).($policyAssignmentId)) {
- $assignmentInfo = 'n/a'
- }
- else {
- $assignmentInfo = ($htCacheAssignmentsPolicyOnResourceGroupsAndResources).($policyAssignmentId)
- }
- }
- else {
- if (-not ($htCacheAssignmentsPolicy).($policyAssignmentId)) {
- $assignmentInfo = 'n/a'
- }
- else {
- $assignmentInfo = ($htCacheAssignmentsPolicy).($policyAssignmentId).Assignment
- }
- }
- }
- }
-
- if ($assignmentinfo -ne 'n/a') {
-
- if ($assignmentinfo.id -like '/subscriptions/*/resourcegroups/*') {
-
- if ($assignmentInfo.properties.policyDefinitionId -like '*/providers/Microsoft.Authorization/policyDefinitions/*') {
- $policyAssignmentsPolicyVariant = 'Policy'
- $policyAssignmentsPolicyVariant4ht = 'policy'
- }
-
- if ($assignmentInfo.properties.policyDefinitionId -like '*/providers/Microsoft.Authorization/policySetDefinitions/*') {
- $policyAssignmentsPolicyVariant = 'PolicySet'
- $policyAssignmentsPolicyVariant4ht = 'policySet'
- }
-
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- $policyAssignmentsPolicyDefinitionId = ($assignmentInfo.properties.policyDefinitionId).ToLower()
- $policyAssignmentspolicyDefinitionIdGuid = $policyAssignmentsPolicyDefinitionId -replace '.*/'
-
- if ($policyAssignmentsPolicyVariant4ht -eq 'policy') {
- if (($htCacheDefinitionsPolicy).($policyAssignmentsPolicyDefinitionId)) {
- $definitionInfo = ($htCacheDefinitionsPolicy).($policyAssignmentsPolicyDefinitionId)
- }
- else {
- $definitionInfo = 'unknown'
- }
- }
- if ($policyAssignmentsPolicyVariant4ht -eq 'policySet') {
- if (($htCacheDefinitionsPolicySet).($policyAssignmentsPolicyDefinitionId)) {
- $definitionInfo = ($htCacheDefinitionsPolicySet).($policyAssignmentsPolicyDefinitionId)
- }
- else {
- $definitionInfo = 'unknown'
- }
- }
-
- }
- else {
- $policyAssignmentsPolicyDefinitionId = ($assignmentInfo.properties.policyDefinitionId).ToLower()
- $policyAssignmentspolicyDefinitionIdGuid = $policyAssignmentsPolicyDefinitionId -replace '.*/'
-
- if ($policyAssignmentsPolicyVariant4ht -eq 'policy') {
- if (($htCacheDefinitionsPolicy).($policyAssignmentsPolicyDefinitionId)) {
- $definitionInfo = ($htCacheDefinitionsPolicy).($policyAssignmentsPolicyDefinitionId)
- }
- else {
- $definitionInfo = 'unknown'
- }
- }
- if ($policyAssignmentsPolicyVariant4ht -eq 'policySet') {
- if (($htCacheDefinitionsPolicySet).($policyAssignmentsPolicyDefinitionId)) {
- $definitionInfo = ($htCacheDefinitionsPolicySet).($policyAssignmentsPolicyDefinitionId)
- }
- else {
- $definitionInfo = 'unknown'
- }
- }
- }
- }
- else {
- if ($assignmentInfo.properties.policyDefinitionId -like '*/providers/Microsoft.Authorization/policyDefinitions/*') {
- $policyAssignmentsPolicyVariant = 'Policy'
- $policyAssignmentsPolicyVariant4ht = 'policy'
- }
- if ($assignmentInfo.properties.policyDefinitionId -like '*/providers/Microsoft.Authorization/policySetDefinitions/*') {
- $policyAssignmentsPolicyVariant = 'PolicySet'
- $policyAssignmentsPolicyVariant4ht = 'policySet'
- }
-
- $policyAssignmentsPolicyDefinitionId = ($assignmentInfo.properties.policyDefinitionId).Tolower()
- $policyAssignmentspolicyDefinitionIdGuid = $policyAssignmentsPolicyDefinitionId -replace '.*/'
-
- if ($policyAssignmentsPolicyVariant4ht -eq 'policy') {
- if (($htCacheDefinitionsPolicy).($policyAssignmentsPolicyDefinitionId)) {
- $definitionInfo = ($htCacheDefinitionsPolicy).($policyAssignmentsPolicyDefinitionId)
- }
- else {
- $definitionInfo = 'unknown'
- }
- }
- if ($policyAssignmentsPolicyVariant4ht -eq 'policySet') {
- if (($htCacheDefinitionsPolicySet).($policyAssignmentsPolicyDefinitionId)) {
- $definitionInfo = ($htCacheDefinitionsPolicySet).($policyAssignmentsPolicyDefinitionId)
- }
- else {
- $definitionInfo = 'unknown'
- }
- }
- }
-
- if ($definitionInfo -eq 'unknown') {
- $policyAssignmentMoreInfo = "unknown definition ($($policyAssignmentsPolicyDefinitionId))"
- }
- else {
- if ($definitionInfo.type -eq 'BuiltIn') {
- $policyAssignmentMoreInfo = "$($definitionInfo.Type) $($policyAssignmentsPolicyVariant): $($definitionInfo.LinkToAzAdvertizer) ($policyAssignmentspolicyDefinitionIdGuid)"
- }
- else {
- $policyAssignmentMoreInfo = "$($definitionInfo.Type) $($policyAssignmentsPolicyVariant): $($definitionInfo.DisplayName -replace '<', '<' -replace '>', '>') ($($policyAssignmentsPolicyDefinitionId))"
- }
- }
- }
- else {
- $policyAssignmentMoreInfo = 'n/a'
- }
-
- }
- else {
- $policyAssignmentMoreInfo = 'n/a'
- }
-
- if ($htRoleAssignmentsForServicePrincipals.($serviceprincipalMI)) {
-
- $arrayMiRoleAssignments = @()
- $helperMiRoleAssignments = $htRoleAssignmentsForServicePrincipals.($serviceprincipalMI).RoleAssignments
-
- foreach ($roleAssignment in $helperMiRoleAssignments) {
- if ($roleAssignment.RoleIsCustom -eq 'False') {
- $arrayMiRoleAssignments += "$(($htCacheDefinitionsRole).($roleAssignment.roleDefinitionId).LinkToAzAdvertizer) ($($roleAssignment.roleassignmentId))"
- }
- else {
- $arrayMiRoleAssignments += "$($roleAssignment.roleDefinitionName -replace '<', '<' -replace '>', '>') ; $($roleAssignment.roleDefinitionId) ($($roleAssignment.roleassignmentId))"
- }
- }
- $miRoleAssignments = "$(($arrayMiRoleAssignments).Count) ($($arrayMiRoleAssignments -join ', '))"
- }
-
- $orphanedMI = ''
- if ($miResourceType -eq 'Microsoft.Authorization/policyAssignments') {
- $orphanedMI = 'false'
- if ($htOrphanedSPMI.($serviceprincipalMI)) {
- $orphanedMI = 'true'
- }
- }
-
- @"
-
-$($serviceprincipalMIDetailed.appId)
-$($serviceprincipalMIDetailed.displayName)
-$($serviceprincipalMI)
-$miType
-$miResourceType
-$($serviceprincipalMIDetailed.alternativeNames -join ', ')
-$($policyAssignmentMoreInfo)
-$($miRoleAssignments)
-$userMiAssignedToResourcesCount
-$orphanedMI
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYAADSPManagedIdentities)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$servicePrincipalsOfTypeManagedIdentityCount AAD ServicePrincipals type=ManagedIdentity
-"@)
- }
-
- $endAADSPManagedIdentityLoop = Get-Date
- Write-Host " TenantSummary AAD SP Managed Identities processing duration: $((New-TimeSpan -Start $startAADSPManagedIdentityLoop -End $endAADSPManagedIdentityLoop).TotalMinutes) minutes ($((New-TimeSpan -Start $startAADSPManagedIdentityLoop -End $endAADSPManagedIdentityLoop).TotalSeconds) seconds)"
- #endregion AADSPManagedIdentity
-
- #region AADSPCredExpiry
- if (-not $skipApplications) {
- $startAADSPCredExpiryLoop = Get-Date
- Write-Host ' processing TenantSummary AAD SP Apps CredExpiry'
-
- $servicePrincipalsOfTypeApplicationCount = ($servicePrincipalsOfTypeApplication).Count
-
- if ($servicePrincipalsOfTypeApplicationCount -gt 0) {
- $tfCount = $servicePrincipalsOfTypeApplicationCount
- $htmlTableId = 'TenantSummary_AADSPCredExpiry'
-
- $servicePrincipalsOfTypeApplicationSecretsExpiring = $servicePrincipalsOfTypeApplication.where( { $htAppDetails.($_).appPasswordCredentialsGracePeriodExpiryCount -gt 0 } )
- $servicePrincipalsOfTypeApplicationSecretsExpiringCount = ($servicePrincipalsOfTypeApplicationSecretsExpiring).Count
- $servicePrincipalsOfTypeApplicationCertificatesExpiring = $servicePrincipalsOfTypeApplication.where( { $htAppDetails.($_).appKeyCredentialsGracePeriodExpiryCount -gt 0 } )
- $servicePrincipalsOfTypeApplicationCertificatesExpiringCount = ($servicePrincipalsOfTypeApplicationCertificatesExpiring).Count
- if ($servicePrincipalsOfTypeApplicationSecretsExpiringCount -gt 0 -or $servicePrincipalsOfTypeApplicationCertificatesExpiringCount -gt 0) {
- $warningOrNot = "
"
- }
- else {
- $warningOrNot = "
"
- }
- [void]$htmlTenantSummary.AppendLine(@"
-
$warningOrNot $($servicePrincipalsOfTypeApplicationCount) AAD ServicePrincipals type=Application | $servicePrincipalsOfTypeApplicationSecretsExpiringCount Secrets expire < $($AADServicePrincipalExpiryWarningDays)d | $servicePrincipalsOfTypeApplicationCertificatesExpiringCount Certificates expire < $($AADServicePrincipalExpiryWarningDays)d
-
-
Download CSV
semicolon |
comma
-
-
-
-ApplicationId
-DisplayName
-Notes
-SP ObjectId
-App ObjectId
-Secrets
-Secrets expired
-Secrets expiry <$($AADServicePrincipalExpiryWarningDays)d
-Secrets expiry >$($AADServicePrincipalExpiryWarningDays)d & <2y
-Secrets expiry >2y
-Certs
-Certs expired
-Certs expiry <$($AADServicePrincipalExpiryWarningDays)d
-Certs expiry >$($AADServicePrincipalExpiryWarningDays)d & <2y
-Certs expiry >2y
-
-
-
-"@)
- $htmlSUMMARYAADSPCredExpiry = $null
- $htmlSUMMARYAADSPCredExpiry = foreach ($serviceprincipalApp in $servicePrincipalsOfTypeApplication | Sort-Object) {
- @"
-
-$($htAppDetails.$serviceprincipalApp.spGraphDetails.appId)
-$($htAppDetails.$serviceprincipalApp.spGraphDetails.displayName)
-$($htAppDetails.$serviceprincipalApp.spGraphDetails.notes)
-$($htAppDetails.$serviceprincipalApp.spGraphDetails.Id)
-$($htAppDetails.$serviceprincipalApp.appGraphDetails.Id)
-"@
- if ($htAppDetails.$serviceprincipalApp.appPasswordCredentialsCount) {
- @"
-$($htAppDetails.$serviceprincipalApp.appPasswordCredentialsCount)
-$($htAppDetails.$serviceprincipalApp.appPasswordCredentialsExpiredCount)
-$($htAppDetails.$serviceprincipalApp.appPasswordCredentialsGracePeriodExpiryCount)
-$($htAppDetails.$serviceprincipalApp.appPasswordCredentialsExpiryOKCount)
-$($htAppDetails.$serviceprincipalApp.appPasswordCredentialsExpiryOKMoreThan2YearsCount)
-"@
- }
- else {
- @'
-0
-0
-0
-0
-0
-'@
- }
-
- if ($htAppDetails.$serviceprincipalApp.appKeyCredentialsCount) {
- @"
-$($htAppDetails.$serviceprincipalApp.appKeyCredentialsCount)
-$($htAppDetails.$serviceprincipalApp.appKeyCredentialsExpiredCount)
-$($htAppDetails.$serviceprincipalApp.appKeyCredentialsGracePeriodExpiryCount)
-$($htAppDetails.$serviceprincipalApp.appKeyCredentialsExpiryOKCount)
-$($htAppDetails.$serviceprincipalApp.appKeyCredentialsExpiryOKMoreThan2YearsCount)
-"@
- }
- else {
- @'
-0
-0
-0
-0
-0
-'@
- }
-
- @'
-
-'@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYAADSPCredExpiry)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$servicePrincipalsOfTypeApplicationCount AAD ServicePrincipals type=Application
-"@)
- }
-
- $endAADSPCredExpiryLoop = Get-Date
- Write-Host " TenantSummary AAD SP Apps CredExpiry processing duration: $((New-TimeSpan -Start $startAADSPCredExpiryLoop -End $endAADSPCredExpiryLoop).TotalMinutes) minutes ($((New-TimeSpan -Start $startAADSPCredExpiryLoop -End $endAADSPCredExpiryLoop).TotalSeconds) seconds)"
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No information on AAD ServicePrincipals type=Application as Guest account does not have enough permissions
-'@)
- }
- #endregion AADSPCredExpiry
-
- #region AADSPExternalSP
- Write-Host ' processing TenantSummary AAD External ServicePrincipals'
- $startAADSPExternalSP = Get-Date
-
- $htRoleAssignmentsForServicePrincipalsRgRes = @{}
- $roleAssignmentsForServicePrincipalsRgRes = (((($htCacheAssignmentsRBACOnResourceGroupsAndResources).values).where( { $_.ObjectType -eq 'ServicePrincipal' })) | Sort-Object -Property RoleAssignmentId -Unique)
- foreach ($spWithRoleAssignment in $roleAssignmentsForServicePrincipalsRgRes | Group-Object -Property ObjectId) {
- if (-not $htRoleAssignmentsForServicePrincipalsRgRes.($spWithRoleAssignment.Name)) {
- $htRoleAssignmentsForServicePrincipalsRgRes.($spWithRoleAssignment.Name) = @{}
- $htRoleAssignmentsForServicePrincipalsRgRes.($spWithRoleAssignment.Name).RoleAssignments = $spWithRoleAssignment.group
- }
- }
-
- $appsWithOtherOrgId = $htServicePrincipals.Keys.where( { $htServicePrincipals.($_).servicePrincipalType -eq 'Application' -and $htServicePrincipals.($_).appOwnerOrganizationId -ne $azAPICallConf['checkContext'].Tenant.Id } )
- $appsWithOtherOrgIdCount = ($appsWithOtherOrgId).Count
-
- if ($appsWithOtherOrgIdCount -gt 0) {
- $tfCount = $appsWithOtherOrgIdCount
- $htmlTableId = 'TenantSummary_AADSPExternal'
-
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- $abbr = "
"
- }
- else {
- $abbr = ''
- }
- [void]$htmlTenantSummary.AppendLine(@"
-
$($appsWithOtherOrgIdCount) External (appOwnerOrganizationId) AAD ServicePrincipals type=Application
-
-
Download CSV
semicolon |
comma
-
-
-
-ApplicationId
-DisplayName
-SP ObjectId
-OrganizationId
-Role assignments$($abbr)
-
-
-
-"@)
- $htmlSUMMARYAADSPExternal = $null
- $htmlSUMMARYAADSPExternal = foreach ($serviceprincipalApp in $appsWithOtherOrgId | Sort-Object) {
- $arrayRoleAssignments4ExternalApp = [System.Collections.ArrayList]@()
- $roleAssignmentsMgSub = $htRoleAssignmentsForServicePrincipals.($serviceprincipalApp).RoleAssignments
- $roleAssignmentsMgSubCount = ($roleAssignmentsMgSub).Count
- $roleAssignments4ExternalApp = 'n/a'
- if ($roleAssignmentsMgSubCount -gt 0) {
- $roleAssignments4ExternalApp = $roleAssignmentsMgSubCount
- }
- $roleAssignmentsRgRes = $htRoleAssignmentsForServicePrincipalsRgRes.($serviceprincipalApp).RoleAssignments
- $roleAssignmentsRgResCount = ($roleAssignmentsRgRes).Count
- if ($roleAssignmentsRgResCount -gt 0) {
- foreach ($roleAssignmentRgRes in $roleAssignmentsRgRes) {
- $null = $arrayRoleAssignments4ExternalApp.Add([PSCustomObject]@{
- roleAssignmentId = $roleAssignmentRgRes.RoleAssignmentId
- })
- }
- $roleAssignments4ExternalApp = "$roleAssignmentsRgResCount ($($arrayRoleAssignments4ExternalApp.roleAssignmentId -join ', '))"
- }
-
- @"
-
-$($htServicePrincipals.($serviceprincipalApp).appId)
-$($htServicePrincipals.($serviceprincipalApp).displayName)
-$($htServicePrincipals.($serviceprincipalApp).id)
-$($htServicePrincipals.($serviceprincipalApp).appOwnerOrganizationId)
-$roleAssignments4ExternalApp
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYAADSPExternal)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$appsWithOtherOrgIdCount External (appOwnerOrganizationId) AAD ServicePrincipals type=Application
-"@)
- }
-
- $endAADSPExternalSP = Get-Date
- Write-Host " TenantSummary AAD External ServicePrincipals processing duration: $((New-TimeSpan -Start $startAADSPExternalSP -End $endAADSPExternalSP).TotalMinutes) minutes ($((New-TimeSpan -Start $startAADSPExternalSP -End $endAADSPExternalSP).TotalSeconds) seconds)"
- #endregion AADSPExternalSP
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- #endregion tenantSummaryAAD
-
- showMemoryUsage
-
- #region tenantSummaryConsumption
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-
Customize your Azure environment optimizations (Cost, Reliability & more) with Azure Optimization Engine (AOE)
-'@)
-
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- $startConsumption = Get-Date
- Write-Host ' processing TenantSummary Consumption'
-
- if (($arrayConsumptionData | Measure-Object).Count -gt 0) {
- $tfCount = ($arrayConsumptionData | Measure-Object).Count
- $htmlTableId = 'TenantSummary_Consumption'
- [void]$htmlTenantSummary.AppendLine(@"
-
Total cost $($arrayTotalCostSummary -join "$CsvDelimiterOpposite ") last $AzureConsumptionPeriod days ($azureConsumptionStartDate - $azureConsumptionEndDate)
-
-
Download CSV
semicolon |
comma
-
-
-
-ChargeType
-ResourceType
-Category
-ResourceCount
-Cost ($($AzureConsumptionPeriod)d)
-Currency
-Subscriptions
-
-
-
-"@)
- $htmlSUMMARYConsumption = $null
- $htmlSUMMARYConsumption = foreach ($consumptionLine in $arrayConsumptionData) {
- @"
-
-$($consumptionLine.ConsumedServiceChargeType)
-$($consumptionLine.ResourceType)
-$($consumptionLine.ConsumedServiceCategory)
-$($consumptionLine.ConsumedServiceInstanceCount)
-$($consumptionLine.ConsumedServiceCost)
-$($consumptionLine.ConsumedServiceCurrency)
-$($consumptionLine.ConsumedServiceSubscriptions)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYConsumption)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No information on Consumption
-'@)
- }
-
- $endConsumption = Get-Date
- Write-Host " TenantSummary Consumption processing duration: $((New-TimeSpan -Start $startConsumption -End $endConsumption).TotalMinutes) minutes ($((New-TimeSpan -Start $startConsumption -End $endConsumption).TotalSeconds) seconds)"
-
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@'
-
No information on Consumption as switch parameter -DoAzureConsumption was not applied
-'@)
- }
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- #endregion tenantSummaryConsumption
-
- showMemoryUsage
-
- #region tenantSummaryChangeTracking
- Write-Host ' processing TenantSummary ChangeTracking'
- $startChangeTracking = Get-Date
- $xdaysAgo = (Get-Date).AddDays(-$ChangeTrackingDays)
-
- #region ctpolicydata
- Write-Host ' processing Policy'
- $customPolicyCreatedOrUpdated = ($customPoliciesDetailed.where( { (-not [string]::IsNullOrEmpty($_.CreatedOn) -and [datetime]$_.CreatedOn -gt $xdaysAgo) -or (-not [string]::IsNullOrEmpty($_.UpdatedOn) -and [datetime]$_.UpdatedOn -gt $xdaysAgo) }))
- $customPolicyCreatedOrUpdatedCount = $customPolicyCreatedOrUpdated.Count
- $customPolicyCreatedMgSub = ($customPolicyCreatedOrUpdated.where( { -not [string]::IsNullOrEmpty($_.CreatedOn) -and [datetime]$_.CreatedOn -gt $xdaysAgo }))
- $customPolicyCreatedMg = ($customPolicyCreatedMgSub.where( { $_.Scope -eq 'Mg' }))
- $customPolicyCreatedMgCount = ($customPolicyCreatedMg).count
- $customPolicyCreatedSub = ($customPolicyCreatedMgSub.where( { $_.Scope -eq 'Sub' }))
- $customPolicyCreatedSubCount = ($customPolicyCreatedSub).count
-
- $customPolicyUpdatedMgSub = ($customPolicyCreatedOrUpdated.where( { -not [string]::IsNullOrEmpty($_.UpdatedOn) -and [datetime]$_.UpdatedOn -gt $xdaysAgo }))
- $customPolicyUpdatedMg = ($customPolicyUpdatedMgSub.where( { $_.Scope -eq 'Mg' }))
- $customPolicyUpdatedMgCount = ($customPolicyUpdatedMg).count
- $customPolicyUpdatedSub = ($customPolicyUpdatedMgSub.where( { $_.Scope -eq 'Sub' }))
- $customPolicyUpdatedSubCount = ($customPolicyUpdatedSub).count
- #endregion ctpolicydata
-
- #region ctpolicySetdata
- Write-Host ' processing PolicySet'
- $customPolicySetCreatedOrUpdated = ($customPolicySetsDetailed.where( { (-not [string]::IsNullOrEmpty($_.CreatedOn) -and [datetime]$_.CreatedOn -gt $xdaysAgo) -or (-not [string]::IsNullOrEmpty($_.UpdatedOn) -and [datetime]$_.UpdatedOn -gt $xdaysAgo) }))
- $customPolicySetCreatedOrUpdatedCount = $customPolicySetCreatedOrUpdated.Count
-
- $customPolicySetCreatedMgSub = ($customPolicySetCreatedOrUpdated.where( { -not [string]::IsNullOrEmpty($_.CreatedOn) -and [datetime]$_.CreatedOn -gt $xdaysAgo }))
- $customPolicySetCreatedMg = ($customPolicySetCreatedMgSub.where( { $_.Scope -eq 'Mg' }))
- $customPolicySetCreatedMgCount = ($customPolicySetCreatedMg).count
- $customPolicySetCreatedSub = ($customPolicySetCreatedMgSub.where( { $_.Scope -eq 'Sub' }))
- $customPolicySetCreatedSubCount = ($customPolicySetCreatedSub).count
-
- $customPolicySetUpdatedMgSub = ($customPolicySetCreatedOrUpdated.where( { -not [string]::IsNullOrEmpty($_.UpdatedOn) -and [datetime]$_.UpdatedOn -gt $xdaysAgo }))
- $customPolicySetUpdatedMg = ($customPolicySetUpdatedMgSub.where( { $_.Scope -eq 'Mg' }))
- $customPolicySetUpdatedMgCount = ($customPolicySetUpdatedMg).count
- $customPolicySetUpdatedSub = ($customPolicySetUpdatedMgSub.where( { $_.Scope -eq 'Sub' }))
- $customPolicySetUpdatedSubCount = ($customPolicySetUpdatedSub).count
- #endregion ctpolicySetdata
-
- #region ctpolicyAssignmentsData
- Write-Host ' processing Policy assignment'
- $policyAssignmentsCreatedOrUpdated = (($arrayPolicyAssignmentsEnriched.where( { $_.Inheritance -notlike 'inherited*' } )).where( { (-not [string]::IsNullOrEmpty($_.CreatedOn) -and [datetime]$_.CreatedOn -gt $xdaysAgo) -or (-not [string]::IsNullOrEmpty($_.UpdatedOn) -and [datetime]$_.UpdatedOn -gt $xdaysAgo) }))
- $policyAssignmentsCreatedOrUpdatedCount = $policyAssignmentsCreatedOrUpdated.Count
-
- $policyAssignmentsCreatedMgSub = $policyAssignmentsCreatedOrUpdated.where( { (-not [string]::IsNullOrEmpty($_.CreatedOn) -and [datetime]$_.CreatedOn -gt $xdaysAgo) })
- $policyAssignmentsCreatedMg = ($policyAssignmentsCreatedMgSub.where( { $_.mgOrSubOrRG -eq 'Mg' }))
- $policyAssignmentsCreatedMgCount = ($policyAssignmentsCreatedMg).count
- $policyAssignmentsCreatedSub = ($policyAssignmentsCreatedMgSub.where( { $_.mgOrSubOrRG -eq 'Sub' }))
- $policyAssignmentsCreatedSubCount = ($policyAssignmentsCreatedSub).count
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- $policyAssignmentsCreatedRg = ($policyAssignmentsUpdatedMgSub.where( { $_.mgOrSubOrRG -eq 'RG' }))
- $policyAssignmentsCreatedRgCount = ($policyAssignmentsCreatedRg).count
- }
-
- $policyAssignmentsUpdatedMgSub = $policyAssignmentsCreatedOrUpdated.where( { (-not [string]::IsNullOrEmpty($_.UpdatedOn) -and [datetime]$_.UpdatedOn -gt $xdaysAgo) })
- $policyAssignmentsUpdatedMg = ($policyAssignmentsUpdatedMgSub.where( { $_.mgOrSubOrRG -eq 'Mg' }))
- $policyAssignmentsUpdatedMgCount = ($policyAssignmentsUpdatedMg).count
- $policyAssignmentsUpdatedSub = ($policyAssignmentsUpdatedMgSub.where( { $_.mgOrSubOrRG -eq 'Sub' }))
- $policyAssignmentsUpdatedSubCount = ($policyAssignmentsUpdatedSub).count
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- $policyAssignmentsUpdatedRg = ($policyAssignmentsUpdatedMgSub.where( { $_.mgOrSubOrRG -eq 'RG' }))
- $policyAssignmentsUpdatedRgCount = ($policyAssignmentsUpdatedRg).count
- }
-
-
- if ($customPolicyCreatedOrUpdatedCount -gt 0 -or $customPolicySetCreatedOrUpdatedCount -gt 0 -or $policyAssignmentsCreatedOrUpdatedCount -gt 0) {
- $ctContenIndicatorPolicy = 'ctContenPolicyTrue'
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- $policyAssignmentSummaryCt = "(Mg: C:$($policyAssignmentsCreatedMgCount), U:$($policyAssignmentsUpdatedMgCount); Sub: C:$($policyAssignmentsCreatedSubCount), U:$($policyAssignmentsUpdatedSubCount)); RG: C:$($policyAssignmentsCreatedRgCount), U:$($policyAssignmentsUpdatedRgCount)"
- }
- else {
- $policyAssignmentSummaryCt = "(Mg: C:$($policyAssignmentsCreatedMgCount), U:$($policyAssignmentsUpdatedMgCount); Sub: C:$($policyAssignmentsCreatedSubCount), U:$($policyAssignmentsUpdatedSubCount))"
- }
-
- }
- else {
- $ctContenIndicatorPolicy = 'ctContenPolicyFalse'
- $policyAssignmentSummaryCt = ''
- }
- #endregion ctpolicyAssignmentsData
-
- ##RBAC
- #region ctRbacData
- #rbac defs
- Write-Host ' processing RBAC'
- $customRoleDefinitionsCreatedOrUpdated = $tenantCustomRoles.where( { $_.IsCustom -eq $true -and $_.Json.properties.createdOn -gt $xdaysAgo -or $_.Json.properties.updatedOn -gt $xdaysAgo })
- $customRoleDefinitionsCreatedOrUpdatedCount = $customRoleDefinitionsCreatedOrUpdated.Count
-
- #rbac defs created
- Write-Host ' processing RBAC Role definition created'
- $customRoleDefinitionsCreated = $customRoleDefinitionsCreatedOrUpdated.where( { $_.Json.properties.createdOn -gt $xdaysAgo })
- $customRoleDefinitionsCreatedCount = $customRoleDefinitionsCreated.Count
-
- #rbac defs updated
- Write-Host ' processing RBAC Role definition updated'
- $customRoleDefinitionsUpdated = $customRoleDefinitionsCreatedOrUpdated.where( { $_.Json.properties.updatedOn -ne $_.Json.properties.createdOn -and $_.Json.properties.updatedOn -gt $xdaysAgo })
- $customRoleDefinitionsUpdatedCount = $customRoleDefinitionsUpdated.Count
- #endregion ctRbacData
-
- #region ctrbacassignments
- #rbac roleassignments
- Write-Host ' processing RBAC Role assignments'
- $roleAssignmentsCreated = ($rbacAll | Sort-Object -Property RoleAssignmentId, ObjectId -Unique).where( { -not [string]::IsNullOrEmpty($_.CreatedOn) -and [datetime]$_.CreatedOn -gt $xdaysAgo })
- $roleAssignmentsCreatedUnique = ($roleAssignmentsCreated | Sort-Object -Property RoleAssignmentId -Unique)
- $roleAssignmentsCreatedCount = ($roleAssignmentsCreated | Sort-Object -Property RoleAssignmentId -Unique).Count
- $roleAssignmentsCreatedImpactedIdentitiesCount = $roleAssignmentsCreated.Count
-
- #rbac assignments createdMg
- $roleAssignmentsCreatedMg = $roleAssignmentsCreatedUnique.where( { $_.ScopeTenOrMgOrSubOrRGOrRes -eq 'MG' -or $_.ScopeTenOrMgOrSubOrRGOrRes -eq 'Ten' })
- $roleAssignmentsCreatedMgCount = $roleAssignmentsCreatedMg.Count
- #rbac assignments createdSub
- $roleAssignmentsCreatedSub = $roleAssignmentsCreatedUnique.where( { $_.ScopeTenOrMgOrSubOrRGOrRes -eq 'Sub' })
- $roleAssignmentsCreatedSubCount = $roleAssignmentsCreatedSub.Count
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- $roleAssignmentsCreatedSubRg = $roleAssignmentsCreatedUnique.where( { $_.ScopeTenOrMgOrSubOrRGOrRes -eq 'RG' })
- $roleAssignmentsCreatedSubRgCount = $roleAssignmentsCreatedSubRg.Count
- $roleAssignmentsCreatedSubRgRes = $roleAssignmentsCreatedUnique.where( { $_.ScopeTenOrMgOrSubOrRGOrRes -eq 'Res' })
- $roleAssignmentsCreatedSubRgResCount = $roleAssignmentsCreatedSubRgRes.Count
- }
-
- if ($customRoleDefinitionsCreatedOrUpdatedCount -gt 0 -or $roleAssignmentsCreatedCount -gt 0) {
- $ctContenIndicatorRBAC = 'ctContenRBACTrue'
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- $rbacAssignmentSummaryCt = "(Mg: $roleAssignmentsCreatedMgCount; Sub: $roleAssignmentsCreatedSubCount; RG: $roleAssignmentsCreatedSubRgCount; Res: $roleAssignmentsCreatedSubRgResCount)"
- }
- else {
- $rbacAssignmentSummaryCt = "(Mg: $roleAssignmentsCreatedMgCount; Sub: $roleAssignmentsCreatedSubCount)"
- }
- }
- else {
- $ctContenIndicatorRBAC = 'ctContenRBACFalse'
- $rbacAssignmentSummaryCt = ''
- }
- #endregion ctrbacassignments
-
-
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- #region ctresources
- Write-Host ' processing Resources'
- $resourcesCreatedOrChanged = $resourcesIdsAll.where( { $_.createdTime -gt $xdaysAgo -or $_.changedTime -gt $xdaysAgo })
- $resourcesCreatedOrChangedCount = $resourcesCreatedOrChanged.Count
-
- $resourcesCreatedAndChanged = $resourcesIdsAll.where( { $_.createdTime -gt $xdaysAgo -and $_.changedTime -gt $xdaysAgo })
- $resourcesCreatedAndChangedCount = $resourcesCreatedAndChanged.Count
-
- $resourcesCreated = $resourcesCreatedOrChanged.where( { $_.createdTime -gt $xdaysAgo })
- $resourcesCreatedCount = $resourcesCreated.Count
- $resourcesChanged = $resourcesCreatedOrChanged.where( { $_.changedTime -gt $xdaysAgo })
- $resourcesChangedCount = $resourcesChanged.Count
-
- if ($resourcesCreatedOrChangedCount -gt 0) {
- $ctContenIndicatorResources = 'ctContenResourcesTrue'
- $resourcesCreatedOrChangedGrouped = $resourcesCreatedOrChanged | Group-Object -Property type
- $resourcesCreatedOrChangedGroupedCount = ($resourcesCreatedOrChangedGrouped | Measure-Object).Count
- }
- else {
- $ctContenIndicatorResources = 'ctContenResourcesFalse'
- }
- #endregion ctresources
- }
-
-
-
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-"@)
-
- #region ctpolicy
- [void]$htmlTenantSummary.AppendLine(@"
-
Policy
-
-"@)
-
- #region ChangeTrackingCustomPolicy
- if ($customPolicyCreatedOrUpdatedCount -gt 0) {
- $tfCount = $customPolicyCreatedOrUpdatedCount
- $htmlTableId = 'TenantSummary_ChangeTrackingCustomPolicy'
- [void]$htmlTenantSummary.AppendLine(@"
-
$customPolicyCreatedOrUpdatedCount Created/Updated custom Policy definitions (Mg: C:$($customPolicyCreatedMgCount), U:$($customPolicyUpdatedMgCount); Sub: C:$($customPolicyCreatedSubCount), U:$($customPolicyUpdatedSubCount))
-
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-Scope Id
-Policy DisplayName
-PolicyId
-Category
-Effect
-Role definitions
-Unique assignments
-Used in PolicySets
-Created/Updated
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlSUMMARYChangeTrackingCustomPolicy = $null
- $htmlSUMMARYChangeTrackingCustomPolicy = foreach ($entry in $customPolicyCreatedOrUpdated | Sort-Object -Property CreatedOn, UpdatedOn -Descending) {
- $createdOnGt = $false
- if ($entry.CreatedOn -ne '') {
- $createdOn = ($entry.CreatedOn)
- if ([datetime]($entry.CreatedOn) -gt $xdaysAgo) {
- $createdOnGt = $true
- }
- }
- else {
- $createdOn = ''
- }
-
- $updatedOnGt = $false
- if ($entry.updatedOn -ne '') {
- $updatedOn = ($entry.UpdatedOn)
- if ([datetime]($entry.UpdatedOn) -gt $xdaysAgo) {
- $updatedOnGt = $true
- }
- $updatedOnGt = $true
- }
- else {
- $updatedOn = ''
- }
-
- $createOnUpdatedOn = $null
- if ($createdOnGt) {
- $createOnUpdatedOn = 'Created'
- }
- if ($updatedOnGt) {
- $createOnUpdatedOn = 'Updated'
- }
- if ($createdOnGt -and $updatedOnGt) {
- $createOnUpdatedOn = 'Created&Updated'
- }
-
- if ($entry.UsedInPolicySetsCount -gt 0) {
- $customPolicyUsedInPolicySets = "$($entry.UsedInPolicySetsCount) ($($entry.UsedInPolicySets))"
- }
- else {
- $customPolicyUsedInPolicySets = $($entry.UsedInPolicySetsCount)
- }
-
- @"
-
-$($entry.Scope)
-$($entry.ScopeId)
-$($entry.PolicyDisplayName -replace '<', '<' -replace '>', '>')
-$($entry.PolicyDefinitionId -replace '<', '<' -replace '>', '>')
-$($entry.PolicyCategory -replace '<', '<' -replace '>', '>')
-$($entry.PolicyEffect)
-$($entry.RoleDefinitions)
-$($entry.UniqueAssignments -replace '<', '<' -replace '>', '>')
-$($customPolicyUsedInPolicySets)
-$createOnUpdatedOn
-$($entry.CreatedOn)
-$($entry.CreatedBy)
-$($entry.UpdatedOn)
-$($entry.UpdatedBy)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYChangeTrackingCustomPolicy)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$customPolicyCreatedOrUpdatedCount Created/Updated custom Policy definitions
-"@)
- }
- #endregion ChangeTrackingCustomPolicy
-
- #region ChangeTrackingCustomPolicySet
- if ($customPolicySetCreatedOrUpdatedCount -gt 0) {
- $tfCount = $customPolicySetCreatedOrUpdatedCount
- $htmlTableId = 'TenantSummary_ChangeTrackingCustomPolicySet'
- [void]$htmlTenantSummary.AppendLine(@"
-
$customPolicySetCreatedOrUpdatedCount Created/Updated custom PolicySet definitions (Mg: C:$($customPolicySetCreatedMgCount), U:$($customPolicySetUpdatedMgCount); Sub: C:$($customPolicySetCreatedSubCount), U:$($customPolicySetUpdatedSubCount))
-
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-ScopeId
-PolicySet DisplayName
-PolicySetId
-Category
-Unique assignments
-Policies used in PolicySet
-Created/Updated
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlSUMMARYChangeTrackingCustomPolicySet = $null
- $htmlSUMMARYChangeTrackingCustomPolicySet = foreach ($entry in $customPolicySetCreatedOrUpdated | Sort-Object -Property CreatedOn, UpdatedOn -Descending) {
- $createdOnGt = $false
- if ($entry.CreatedOn -ne '') {
- $createdOn = ($entry.CreatedOn)
- if ([datetime]($entry.CreatedOn) -gt $xdaysAgo) {
- $createdOnGt = $true
- }
- }
- else {
- $createdOn = ''
- }
-
- $updatedOnGt = $false
- if ($entry.updatedOn -ne '') {
- $updatedOn = ($entry.UpdatedOn)
- if ([datetime]($entry.UpdatedOn) -gt $xdaysAgo) {
- $updatedOnGt = $true
- }
- $updatedOnGt = $true
- }
- else {
- $updatedOn = ''
- }
-
- $createOnUpdatedOn = $null
- if ($createdOnGt) {
- $createOnUpdatedOn = 'Created'
- }
- if ($updatedOnGt) {
- $createOnUpdatedOn = 'Updated'
- }
- if ($createdOnGt -and $updatedOnGt) {
- $createOnUpdatedOn = 'Created&Updated'
- }
-
- @"
-
-$($entry.Scope)
-$($entry.ScopeId)
-$($entry.PolicySetDisplayName -replace '<', '<' -replace '>', '>')
-$($entry.PolicySetDefinitionId -replace '<', '<' -replace '>', '>')
-$($entry.PolicySetCategory -replace '<', '<' -replace '>', '>')
-$($entry.UniqueAssignments -replace '<', '<' -replace '>', '>')
-$($entry.PoliciesUsed)
-$createOnUpdatedOn
-$($entry.CreatedOn)
-$($entry.CreatedBy)
-$($entry.UpdatedOn)
-$($entry.UpdatedBy)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYChangeTrackingCustomPolicySet)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$customPolicySetCreatedOrUpdatedCount Created/Updated custom PolicySet definitions
-"@)
- }
- #endregion ChangeTrackingCustomPolicySet
-
- #region ChangeTrackingPolicyAssignments
- if ($policyAssignmentsCreatedOrUpdatedCount -gt 0) {
- $tfCount = $policyAssignmentsCreatedOrUpdatedCount
- $htmlTableId = 'TenantSummary_ChangeTrackingPolicyAssignments'
- [void]$htmlTenantSummary.AppendLine(@"
-
$policyAssignmentsCreatedOrUpdatedCount Created/Updated Policy assignments ($policyAssignmentSummaryCt)
-
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-Management Group Id
-Management Group Name
-SubscriptionId
-Subscription Name
-Inheritance
-ScopeExcluded
-Exemption applies
-Policy/Set DisplayName
-Policy/Set Description
-Policy/SetId
-Policy/Set
-Type
-Category
-Effect
-Parameters
-Enforcement
-NonCompliance Message
-"@)
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- [void]$htmlTenantSummary.AppendLine(@'
-Policies NonCmplnt
-Policies Compliant
-Resources NonCmplnt
-Resources Compliant
-Resources Conflicting
-'@)
- }
-
- [void]$htmlTenantSummary.AppendLine(@"
-Role/Assignment $noteOrNot
-Assignment DisplayName
-Assignment Description
-AssignmentId
-Created/Updated
-AssignedBy
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlSUMMARYChangeTrackingPolicyAssignments = $null
- $htmlSUMMARYChangeTrackingPolicyAssignments = foreach ($policyAssignment in $policyAssignmentsCreatedOrUpdated | Sort-Object -Property CreatedOn, UpdatedOn -Descending) {
- $createdOnGt = $false
- if ($policyAssignment.CreatedOn -ne '') {
- $createdOn = ($policyAssignment.CreatedOn)
- if ([datetime]($policyAssignment.CreatedOn) -gt $xdaysAgo) {
- $createdOnGt = $true
- }
- }
- else {
- $createdOn = ''
- }
-
- $updatedOnGt = $false
- if ($policyAssignment.updatedOn -ne '') {
- $updatedOn = ($policyAssignment.UpdatedOn)
- if ([datetime]($policyAssignment.UpdatedOn) -gt $xdaysAgo) {
- $updatedOnGt = $true
- }
- $updatedOnGt = $true
- }
- else {
- $updatedOn = ''
- }
-
- $createOnUpdatedOn = $null
- if ($createdOnGt) {
- $createOnUpdatedOn = 'Created'
- }
- if ($updatedOnGt) {
- $createOnUpdatedOn = 'Updated'
- }
- if ($createdOnGt -and $updatedOnGt) {
- $createOnUpdatedOn = 'Created&Updated'
- }
-
- if ($policyAssignment.PolicyType -eq 'Custom') {
- $policyName = ($policyAssignment.PolicyName -replace '<', '<' -replace '>', '>')
- }
- else {
- $policyName = $policyAssignment.PolicyName
- }
-
- @"
-
-$($policyAssignment.mgOrSubOrRG)
-$($policyAssignment.MgId)
-$($policyAssignment.MgName -replace '<', '<' -replace '>', '>')
-$($policyAssignment.SubscriptionId)
-$($policyAssignment.SubscriptionName)
-$($policyAssignment.Inheritance)
-$($policyAssignment.ExcludedScope)
-$($policyAssignment.ExemptionScope)
-$($policyName)
-$($policyAssignment.PolicyDescription -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyId -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyVariant)
-$($policyAssignment.PolicyType)
-$($policyAssignment.PolicyCategory -replace '<', '<' -replace '>', '>')
-$($policyAssignment.Effect)
-$($policyAssignment.PolicyAssignmentParameters)
-$($policyAssignment.PolicyAssignmentEnforcementMode)
-$($policyAssignment.PolicyAssignmentNonComplianceMessages)
-"@
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- @"
-$($policyAssignment.NonCompliantPolicies)
-$($policyAssignment.CompliantPolicies)
-$($policyAssignment.NonCompliantResources)
-$($policyAssignment.CompliantResources)
-$($policyAssignment.ConflictingResources)
-"@
- }
-
- @"
-$($policyAssignment.RelatedRoleAssignments)
-$($policyAssignment.PolicyAssignmentDisplayName -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyAssignmentDescription -replace '<', '<' -replace '>', '>')
-$($policyAssignment.PolicyAssignmentId -replace '<', '<' -replace '>', '>')
-$createOnUpdatedOn
-$($policyAssignment.AssignedBy)
-$($policyAssignment.CreatedOn)
-$($policyAssignment.CreatedBy)
-$($policyAssignment.UpdatedOn)
-$($policyAssignment.UpdatedBy)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYChangeTrackingPolicyAssignments)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$policyAssignmentsCreatedOrUpdatedCount Created/Updated Policy assignments
-"@)
- }
- #endregion ChangeTrackingPolicyAssignments
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
-
- #endregion ctpolicy
-
- #region ctrbac
- [void]$htmlTenantSummary.AppendLine(@"
-
RBAC
-
-"@)
-
- #region ChangeTrackingCustomRoles
- if ($customRoleDefinitionsCreatedOrUpdatedCount -gt 0) {
- $tfCount = $customRoleDefinitionsCreatedOrUpdatedCount
- $htmlTableId = 'TenantSummary_ChangeTrackingCustomRoles'
- [void]$htmlTenantSummary.AppendLine(@"
-
$customRoleDefinitionsCreatedOrUpdatedCount Created/Updated custom Role definitions (Created: $customRoleDefinitionsCreatedCount; Updated: $customRoleDefinitionsUpdatedCount)
-
-
Download CSV
semicolon |
comma
-
-
-
-Role Name
-RoleId
-Assignable Scopes
-Data
-Created/Updated
-CreatedOn
-CreatedBy
-UpdatedOn
-UpdatedBy
-
-
-
-"@)
- $htmlSUMMARYChangeTrackingCustomRoles = $null
- $htmlSUMMARYChangeTrackingCustomRoles = foreach ($entry in $customRoleDefinitionsCreatedOrUpdated | Sort-Object @{Expression = { $_.Json.properties.createdOn } }, @{Expression = { $_.Json.properties.updatedOn } } -Descending) {
- $createdBy = $entry.Json.properties.createdBy
- if ($htIdentitiesWithRoleAssignmentsUnique.($createdBy)) {
- $createdBy = $htIdentitiesWithRoleAssignmentsUnique.($createdBy).details
- }
-
- $createdOn = $entry.Json.properties.createdOn
- $createdOnFormated = $createdOn
- $createdOnUpdatedOn = 'Created'
-
- $updatedOn = $entry.Json.properties.updatedOn
- if ($updatedOn -eq $createdOn) {
- $updatedOnFormated = ''
- $updatedByRemoveNoiseOrNot = ''
- }
- else {
- if ($createdOn -gt $xdaysAgo) {
- $createdOnUpdatedOn = 'Created&Updated'
- }
- else {
- $createdOnUpdatedOn = 'Updated'
- }
- $updatedOnFormated = $updatedOn
- $updatedByRemoveNoiseOrNot = $entry.Json.properties.updatedBy
- if ($htIdentitiesWithRoleAssignmentsUnique.($updatedByRemoveNoiseOrNot)) {
- $updatedByRemoveNoiseOrNot = $htIdentitiesWithRoleAssignmentsUnique.($updatedByRemoveNoiseOrNot).details
- }
- }
-
- @"
-
-$($entry.Name -replace '<', '<' -replace '>', '>')
-$($entry.Id)
-$(($entry.AssignableScopes | Measure-Object).count) ($($entry.AssignableScopes -join "$CsvDelimiterOpposite "))
-$($roleManageData)
-$createdOnUpdatedOn
-$createdOnFormated
-$createdBy
-$updatedOnFormated
-$updatedByRemoveNoiseOrNot
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYChangeTrackingCustomRoles)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$customRoleDefinitionsCreatedOrUpdatedCount Created/Updated custom Role definitions
-"@)
- }
- #endregion ChangeTrackingCustomRoles
-
- #region ChangeTrackingRoleAssignments
- if ($roleAssignmentsCreatedCount -gt 0) {
- $tfCount = $roleAssignmentsCreatedCount
- $htmlTableId = 'TenantSummary_ChangeTrackingRoleAssignments'
- [void]$htmlTenantSummary.AppendLine(@"
-
$roleAssignmentsCreatedCount Created Role assignments $rbacAssignmentSummaryCt (impacted identities: $roleAssignmentsCreatedImpactedIdentitiesCount)
-
-
Download CSV
semicolon |
comma
-
-
-
-Scope
-Role
-Role Id
-Role Type
-Data
-Identity Displayname
-Identity SignInName
-Identity ObjectId
-Identity Type
-Applicability
-Applies through membership
-Group Details
-PIM
-PIM assignment type
-PIM start
-PIM end
-Role AssignmentId
-Related Policy Assignment $noteOrNot
-CreatedOn
-CreatedBy
-
-
-
-"@)
- $htmlSUMMARYChangeTrackingRoleAssignments = $null
- $htmlSUMMARYChangeTrackingRoleAssignments = [System.Text.StringBuilder]::new()
- foreach ($entry in $roleAssignmentsCreated | Sort-Object -Property CreatedOn -Descending) {
- if ($entry.RoleType -eq 'Custom') {
- $roleName = ($entry.Role -replace '<', '<' -replace '>', '>')
- }
- else {
- $roleName = $entry.Role
- }
- [void]$htmlSUMMARYChangeTrackingRoleAssignments.AppendFormat(
- @'
-
-{0}
-{1}
-{2}
-{3}
-{4}
-{5}
-{6}
-{7}
-{8}
-{9}
-{10}
-{11}
-{12}
-{13}
-{14}
-{15}
-{16}
-{17}
-{18}
-{19}
-
-'@, $entry.ScopeTenOrMgOrSubOrRGOrRes,
- $roleName,
- $entry.RoleId,
- $entry.RoleType,
- $entry.RoleDataRelated,
- $entry.ObjectDisplayName,
- $entry.ObjectSignInName,
- $entry.ObjectId,
- $entry.ObjectType,
- $entry.AssignmentType,
- $entry.AssignmentInheritFrom,
- $entry.GroupMembersCount,
- $entry.RoleAssignmentPIMRelated,
- $entry.RoleAssignmentPIMAssignmentType,
- $entry.RoleAssignmentPIMAssignmentSlotStart,
- $entry.RoleAssignmentPIMAssignmentSlotEnd,
- $entry.RoleAssignmentId,
- #($entry.RbacRelatedPolicyAssignment -replace '<', '<' -replace '>', '>'),
- $entry.RbacRelatedPolicyAssignment,
- $entry.CreatedOn,
- $entry.CreatedBy
- )
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYChangeTrackingRoleAssignments)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$customRoleDefinitionsCreatedOrUpdatedCount Created/Updated custom Role definitions
-"@)
- }
- #endregion ChangeTrackingRoleAssignments
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
-
- #endregion ctrbac
-
- if ($azAPICallConf['htParameters'].NoResources -eq $false) {
- #region ctresources
- [void]$htmlTenantSummary.AppendLine(@"
-
Resources
-
-"@)
-
- #region ChangeTrackingResources
- if ($resourcesCreatedOrChangedCount -gt 0) {
- $tfCount = $resourcesCreatedOrChangedGroupedCount
- $htmlTableId = 'TenantSummary_ChangeTrackingResources'
- [void]$htmlTenantSummary.AppendLine(@"
-
$resourcesCreatedOrChangedCount Created/Changed Resources ($resourcesCreatedOrChangedGroupedCount ResourceTypes) (Created&Changed: $resourcesCreatedAndChangedCount; Created: $resourcesCreatedCount; Changed: $resourcesChangedCount)
-
-
Download CSV
semicolon |
comma
-
-
-
-ResourceType
-Resource Count
-Created&Changed
-Created&Changed Subs
-Created
-Created Subs
-Changed
-Changed Subs
-
-
-
-"@)
- $htmlSUMMARYChangeTrackingResources = $null
- $htmlSUMMARYChangeTrackingResources = foreach ($entry in $resourcesCreatedOrChangedGrouped) {
- $createdAndChanged = $entry.group.where( { $_.createdTime -gt $xdaysAgo -and $_.changedTime -gt $xdaysAgo })
- $createdAndChangedCount = $createdAndChanged.Count
- $createdAndChangedInSubscriptionsCount = ($createdAndChanged | Group-Object -Property subscriptionId | Measure-Object).Count
-
- $created = $entry.group.where( { $_.createdTime -gt $xdaysAgo })
- $createdCount = $created.Count
- $createdInSubscriptionsCount = ($created | Group-Object -Property subscriptionId | Measure-Object).Count
-
- $changed = $entry.group.where( { $_.changedTime -gt $xdaysAgo })
- $changedCount = $changed.Count
- $changedInSubscriptionsCount = ($changed | Group-Object -Property subscriptionId | Measure-Object).Count
-
- @"
-
-$($entry.Name)
-$($entry.Count)
-$($createdAndChangedCount)
-$($createdAndChangedInSubscriptionsCount)
-$($createdCount)
-$($createdInSubscriptionsCount)
-$($changedCount)
-$($changedInSubscriptionsCount)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYChangeTrackingResources)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
$resourcesCreatedOrChangedCount Created/Changed Resources
-"@)
- }
- #endregion ChangeTrackingResources
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
-
- #endregion ctresources
- }
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
-
- $endChangeTracking = Get-Date
- Write-Host " ChangeTracking duration: $((New-TimeSpan -Start $startChangeTracking -End $endChangeTracking).TotalMinutes) minutes ($((New-TimeSpan -Start $startChangeTracking -End $endChangeTracking).TotalSeconds) seconds)"
- #endregion tenantSummaryChangeTracking
-
- showMemoryUsage
-
- #region tenantSummaryNaming
- [void]$htmlTenantSummary.AppendLine(@'
-
-
-'@)
-
- $startSUMMARYNaming = Get-Date
- Write-Host ' processing TenantSummary Findings'
-
-
- $namingPolicyCount = $htNamingValidation.Policy.values.count
- if ($namingPolicyCount -gt 0) {
- $tfCount = $namingPolicyCount
- $htmlTableId = 'TenantSummary_NamingPolicy'
- [void]$htmlTenantSummary.AppendLine(@"
-
Policy $($namingPolicyCount) Naming findings
-
-
Download CSV
semicolon |
comma
-
-
-
-Id
-Name
-Name Invalid chars
-DisplayName
-DisplayName Invalid chars
-
-
-
-"@)
- $htmlSUMMARYNamingPolicy = $null
- $cnter = 0
- $htmlSUMMARYNamingPolicy = foreach ($key in $htNamingValidation.Policy.Keys | Sort-Object) {
- $id = $key -replace '<', '<' -replace '>', '>'
- if ($htNamingValidation.Policy.($key).name) {
- $name = $htNamingValidation.Policy.($key).name -replace '<', '<' -replace '>', '>'
- $nameInvalidChars = $htNamingValidation.Policy.($key).nameInvalidChars -replace '<', '<' -replace '>', '>'
- }
- else {
- $name = ''
- $nameInvalidChars = ''
- }
-
- if ($htNamingValidation.Policy.($key).displayName) {
- $displayName = $htNamingValidation.Policy.($key).displayName -replace '<', '<' -replace '>', '>'
- $displayNameInvalidChars = $htNamingValidation.Policy.($key).displayNameInvalidChars -replace '<', '<' -replace '>', '>'
- }
- else {
- $displayName = ''
- $displayNameInvalidChars = ''
- }
-
-
- @"
-
-$($id)
-$($name)
-$($nameInvalidChars)
-$($displayName)
-$($displayNameInvalidChars)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYNamingPolicy)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Policy $($namingPolicyCount) Naming findings
-"@)
- }
-
- $namingPolicySetCount = $htNamingValidation.PolicySet.values.count
- if ($namingPolicySetCount -gt 0) {
- $tfCount = $namingPolicySetCount
- $htmlTableId = 'TenantSummary_NamingPolicySet'
- [void]$htmlTenantSummary.AppendLine(@"
-
PolicySet $($namingPolicySetCount) Naming findings
-
-
Download CSV
semicolon |
comma
-
-
-
-Id
-Name
-Name Invalid chars
-DisplayName
-DisplayName Invalid chars
-
-
-
-"@)
- $htmlSUMMARYNamingPolicySet = $null
- $cnter = 0
- $htmlSUMMARYNamingPolicySet = foreach ($key in $htNamingValidation.PolicySet.Keys | Sort-Object) {
- $id = $key -replace '<', '<' -replace '>', '>'
- if ($htNamingValidation.PolicySet.($key).name) {
- $name = $htNamingValidation.PolicySet.($key).name -replace '<', '<' -replace '>', '>'
- $nameInvalidChars = $htNamingValidation.PolicySet.($key).nameInvalidChars -replace '<', '<' -replace '>', '>'
- }
- else {
- $name = ''
- $nameInvalidChars = ''
- }
-
- if ($htNamingValidation.PolicySet.($key).displayName) {
- $displayName = $htNamingValidation.PolicySet.($key).displayName -replace '<', '<' -replace '>', '>'
- $displayNameInvalidChars = $htNamingValidation.PolicySet.($key).displayNameInvalidChars -replace '<', '<' -replace '>', '>'
- }
- else {
- $displayName = ''
- $displayNameInvalidChars = ''
- }
-
-
- @"
-
-$($id)
-$($name)
-$($nameInvalidChars)
-$($displayName)
-$($displayNameInvalidChars)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYNamingPolicySet)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
PolicySet $($namingPolicySetCount) Naming findings
-"@)
- }
-
- $namingPolicyAssignmentCount = $htNamingValidation.PolicyAssignment.values.count
- if ($namingPolicyAssignmentCount -gt 0) {
- $tfCount = $namingPolicyAssignmentCount
- $htmlTableId = 'TenantSummary_NamingPolicyAssignment'
- [void]$htmlTenantSummary.AppendLine(@"
-
Policy assignment $($namingPolicyAssignmentCount) Naming findings
-
-
Download CSV
semicolon |
comma
-
-
-
-Id
-Name
-Name Invalid chars
-DisplayName
-DisplayName Invalid chars
-
-
-
-"@)
- $htmlSUMMARYNamingPolicyAssignment = $null
- $cnter = 0
- $htmlSUMMARYNamingPolicyAssignment = foreach ($key in $htNamingValidation.PolicyAssignment.Keys | Sort-Object) {
- $id = $key -replace '<', '<' -replace '>', '>'
- if ($htNamingValidation.PolicyAssignment.($key).name) {
- $name = $htNamingValidation.PolicyAssignment.($key).name -replace '<', '<' -replace '>', '>'
- $nameInvalidChars = $htNamingValidation.PolicyAssignment.($key).nameInvalidChars -replace '<', '<' -replace '>', '>'
- }
- else {
- $name = ''
- $nameInvalidChars = ''
- }
-
- if ($htNamingValidation.PolicyAssignment.($key).displayName) {
- $displayName = $htNamingValidation.PolicyAssignment.($key).displayName -replace '<', '<' -replace '>', '>'
- $displayNameInvalidChars = $htNamingValidation.PolicyAssignment.($key).displayNameInvalidChars -replace '<', '<' -replace '>', '>'
- }
- else {
- $displayName = ''
- $displayNameInvalidChars = ''
- }
-
-
- @"
-
-$($id)
-$($name)
-$($nameInvalidChars)
-$($displayName)
-$($displayNameInvalidChars)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYNamingPolicyAssignment)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Policy assignment $($namingPolicyAssignmentCount) Naming findings
-"@)
- }
-
- $namingManagementGroupCount = $htNamingValidation.ManagementGroup.values.count
- if ($namingManagementGroupCount -gt 0) {
- $tfCount = $namingManagementGroupCount
- $htmlTableId = 'TenantSummary_NamingManagementGroup'
- [void]$htmlTenantSummary.AppendLine(@"
-
Management Group $($namingManagementGroupCount) Naming findings
-
-
Download CSV
semicolon |
comma
-
-
-
-Id
-Name
-Name Invalid chars
-
-
-
-"@)
- $htmlSUMMARYNamingManagementGroup = $null
- $cnter = 0
- $htmlSUMMARYNamingManagementGroup = foreach ($key in $htNamingValidation.ManagementGroup.Keys | Sort-Object) {
- $id = $key -replace '<', '<' -replace '>', '>'
- if ($htNamingValidation.ManagementGroup.($key).name) {
- $name = $htNamingValidation.ManagementGroup.($key).name -replace '<', '<' -replace '>', '>'
- $nameInvalidChars = $htNamingValidation.ManagementGroup.($key).nameInvalidChars -replace '<', '<' -replace '>', '>'
- }
- else {
- $name = ''
- $nameInvalidChars = ''
- }
-
- @"
-
-$($id)
-$($name)
-$($nameInvalidChars)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYNamingManagementGroup)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Management Group $($namingManagementGroupCount) Naming findings
-"@)
- }
-
-
- $namingSubscriptionCount = $htNamingValidation.Subscription.values.count
- if ($namingSubscriptionCount -gt 0) {
- $tfCount = $namingSubscriptionCount
- $htmlTableId = 'TenantSummary_NamingSubscription'
- [void]$htmlTenantSummary.AppendLine(@"
-
Subscription $($namingSubscriptionCount) Naming findings
-
-
Download CSV
semicolon |
comma
-
-
-
-Id
-DisplayName
-DisplayName Invalid chars
-
-
-
-"@)
- $htmlSUMMARYNamingSubscription = $null
- $htmlSUMMARYNamingSubscription = foreach ($key in $htNamingValidation.Subscription.Keys | Sort-Object) {
-
- if ($htNamingValidation.Subscription.($key).displayName) {
- $displayName = $htNamingValidation.Subscription.($key).displayName -replace '<', '<' -replace '>', '>'
- $displayNameInvalidChars = $htNamingValidation.Subscription.($key).displayNameInvalidChars -replace '<', '<' -replace '>', '>'
- }
- else {
- $displayName = ''
- $displayNameInvalidChars = ''
- }
-
- @"
-
-$($key)
-$($displayName)
-$($displayNameInvalidChars)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYNamingSubscription)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
Subscription $($namingSubscriptionCount) Naming findings
-"@)
- }
-
-
- $namingRoleCount = $htNamingValidation.Role.values.count
- if ($namingRoleCount -gt 0) {
- $tfCount = $namingRoleCount
- $htmlTableId = 'TenantSummary_NamingRole'
- [void]$htmlTenantSummary.AppendLine(@"
-
RBAC $($namingRoleCount) Naming findings
-
-
Download CSV
semicolon |
comma
-
-
-
-Id
-Name
-Name Invalid chars
-
-
-
-"@)
- $htmlSUMMARYNamingRole = $null
- $htmlSUMMARYNamingRole = foreach ($key in $htNamingValidation.Role.Keys | Sort-Object) {
-
- if ($htNamingValidation.Role.($key).roleName) {
- $roleName = $htNamingValidation.Role.($key).roleName -replace '<', '<' -replace '>', '>'
- $roleNameInvalidChars = $htNamingValidation.Role.($key).roleNameInvalidChars -replace '<', '<' -replace '>', '>'
- }
- else {
- $roleName = ''
- $roleNameInvalidChars = ''
- }
-
- @"
-
-$($key)
-$($roleName)
-$($roleNameInvalidChars)
-
-"@
- }
- [void]$htmlTenantSummary.AppendLine($htmlSUMMARYNamingRole)
- [void]$htmlTenantSummary.AppendLine(@"
-
-
-
-
-
-"@)
- }
- else {
- [void]$htmlTenantSummary.AppendLine(@"
-
RBAC $($namingRoleCount) Naming Findings
-"@)
- }
-
- $endSUMMARYNaming = Get-Date
- Write-Host " SUMMARYMGs duration: $((New-TimeSpan -Start $startSUMMARYNaming -End $endSUMMARYNaming).TotalMinutes) minutes ($((New-TimeSpan -Start $startSUMMARYNaming -End $endSUMMARYNaming).TotalSeconds) seconds)"
-
- [void]$htmlTenantSummary.AppendLine(@'
-
-'@)
- #endregion tenantSummaryNaming
-
- $script:html += $htmlTenantSummary
- $htmlTenantSummary = $null
- $script:html | Add-Content -Path "$($outputPath)$($DirectorySeparatorChar)$($fileName).html" -Encoding utf8 -Force
- $script:html = $null
-}
-function removeInvalidFileNameChars {
- param(
- [Parameter(Mandatory = $true,
- Position = 0,
- ValueFromPipeline = $true,
- ValueFromPipelineByPropertyName = $true)]
- [String]$Name
- )
- if ($Name -like '`[Deprecated`]:*') {
- $Name = $Name -replace '\[Deprecated\]\:', '[Deprecated]'
- }
- if ($Name -like '`[Preview`]:*') {
- $Name = $Name -replace '\[Preview\]\:', '[Preview]'
- }
- if ($Name -like '`[ASC Private Preview`]:*') {
- $Name = $Name -replace '\[ASC Private Preview\]\:', '[ASC Private Preview]'
- }
- return ($Name -replace ':', '_' -replace '/', '_' -replace '\\', '_' -replace '<', '_' -replace '>', '_' -replace '\*', '_' -replace '\?', '_' -replace '\|', '_' -replace '"', '_')
-}
-function ResolveObjectIds {
- [CmdletBinding()]Param(
- [object]
- $objectIds,
-
- [switch]
- $showActivity
- )
-
- $arrayObjectIdsToCheck = @()
- $arrayObjectIdsToCheck = foreach ($objectToCheckIfAlreadyResolved in $objectIds) {
- if (-not $htPrincipals.($objectToCheckIfAlreadyResolved)) {
- $objectToCheckIfAlreadyResolved
- }
- else {
- #Write-Host "$objectToCheckIfAlreadyResolved already resolved"
- }
- }
-
- if ($arrayObjectIdsToCheck.Count -gt 0) {
-
- $counterBatch = [PSCustomObject] @{ Value = 0 }
- $batchSize = 1000
- $ObjectBatch = $arrayObjectIdsToCheck | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
- $ObjectBatchCount = ($ObjectBatch | Measure-Object).Count
- $batchCnt = 0
-
- foreach ($batch in $ObjectBatch) {
- $batchCnt++
- $objectsToProcess = '"{0}"' -f ($batch.Group.where({ testGuid $_ }) -join '","')
- $currentTask = " Resolving ObjectIds - Batch #$batchCnt/$($ObjectBatchCount) ($(($batch.Group).Count))"
- if ($showActivity) {
- Write-Host $currentTask
- }
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/directoryObjects/getByIds"
- $method = 'POST'
- $body = @"
- {
- "ids":[$($objectsToProcess)]
- }
-"@
- $resolveObjectIds = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -currentTask $currentTask
-
- foreach ($identity in $resolveObjectIds) {
- if (-not $htPrincipals.($identity.id)) {
- $arrayIdentityObject = [System.Collections.ArrayList]@()
- if ($identity.'@odata.type' -eq '#microsoft.graph.user') {
- if ($identity.userType -eq 'Guest') {
- $script:htUserTypesGuest.($identity.id) = @{}
- $script:htUserTypesGuest.($identity.id).userType = 'Guest'
- }
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'User'
- userType = $identity.userType
- id = $identity.id
- displayName = $identity.displayName
- signInName = $identity.userPrincipalName
- })
- }
- if ($identity.'@odata.type' -eq '#microsoft.graph.group') {
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'Group'
- id = $identity.id
- displayName = $identity.displayName
- })
- }
- if ($identity.'@odata.type' -eq '#microsoft.graph.servicePrincipal') {
- if ($identity.servicePrincipalType -eq 'Application') {
- if ($identity.appOwnerOrganizationId -eq $azAPICallConf['checkContext'].Tenant.Id) {
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'ServicePrincipal'
- spTypeConcatinated = 'SP APP INT'
- servicePrincipalType = $identity.servicePrincipalType
- id = $identity.id
- appid = $identity.appId
- displayName = $identity.displayName
- appOwnerOrganizationId = $identity.appOwnerOrganizationId
- alternativeNames = $identity.alternativeNames
- })
- }
- else {
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'ServicePrincipal'
- spTypeConcatinated = 'SP APP EXT'
- servicePrincipalType = $identity.servicePrincipalType
- id = $identity.id
- appid = $identity.appId
- displayName = $identity.displayName
- appOwnerOrganizationId = $identity.appOwnerOrganizationId
- alternativeNames = $identity.alternativeNames
- })
- }
- }
- elseif ($identity.servicePrincipalType -eq 'ManagedIdentity') {
- $miType = 'unknown'
- if ($identity.alternativeNames) {
- foreach ($altName in $identity.alternativeNames) {
- if ($altName -like 'isExplicit=*') {
- $splitAltName = $altName.split('=')
- if ($splitAltName[1] -eq 'true') {
- $miType = 'Usr'
- }
- if ($splitAltName[1] -eq 'false') {
- $miType = 'Sys'
- }
- }
- }
- }
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'ServicePrincipal'
- spTypeConcatinated = "SP MI $miType"
- servicePrincipalType = $identity.servicePrincipalType
- id = $identity.id
- appid = $identity.appId
- displayName = $identity.displayName
- appOwnerOrganizationId = $identity.appOwnerOrganizationId
- alternativeNames = $identity.alternativeNames
- })
- }
- else {
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'servicePrincipal'
- spTypeConcatinated = "SP $($identity.servicePrincipalType)"
- servicePrincipalType = $identity.servicePrincipalType
- id = $identity.id
- appid = $identity.appId
- displayName = $identity.displayName
- appOwnerOrganizationId = $identity.appOwnerOrganizationId
- alternativeNames = $identity.alternativeNames
- })
- }
- if (-not $htServicePrincipals.($identity.id)) {
- $script:htServicePrincipals.($identity.id) = @{}
- $script:htServicePrincipals.($identity.id) = $arrayIdentityObject
- }
- }
- if (-not $htPrincipals.($identity.id)) {
- $script:htPrincipals.($identity.id) = $arrayIdentityObject
- }
- }
- }
- if ($batch.Group.Count -ne $resolveObjectIds.Count) {
- foreach ($objectId in $batch.Group) {
- if ($resolveObjectIds.id -notcontains $objectId) {
- if (-not $htPrincipals.($objectId)) {
- $arrayIdentityObject = [System.Collections.ArrayList]@()
- $null = $arrayIdentityObject.Add([PSCustomObject]@{
- type = 'Unknown'
- id = $objectId
- })
- $script:htPrincipals.($objectId) = $arrayIdentityObject
- }
- else {
- #Write-Host "$($objectId) was already collected"
- }
- }
- }
- }
- }
- }
-}
-function runInfo {
- #region RunInfo
- Write-Host 'Run Info:'
- if ($HierarchyMapOnly) {
- Write-Host ' Creating HierarchyMap only' -ForegroundColor Green
- }
- else {
- $script:paramsUsed = $Null
- $startTimeUTC = ((Get-Date).ToUniversalTime()).ToString('dd-MMM-yyyy HH:mm:ss')
- $script:paramsUsed += "Date: $startTimeUTC (UTC); Version: $ProductVersion
"
-
- if ($azAPICallConf['htParameters'].accountType -eq 'ServicePrincipal') {
- $script:paramsUsed += "ExecutedBy: $($azAPICallConf['checkContext'].Account.Id) (App/ClientId) ($($azAPICallConf['htParameters'].accountType))
"
- }
- elseif ($azAPICallConf['htParameters'].accountType -eq 'ManagedService') {
- $script:paramsUsed += "ExecutedBy: $($azAPICallConf['checkContext'].Account.Id) (Id) ($($azAPICallConf['htParameters'].accountType))
"
- }
- elseif ($azAPICallConf['htParameters'].accountType -eq 'ClientAssertion') {
- $script:paramsUsed += "ExecutedBy: $($azAPICallConf['checkContext'].Account.Id) (App/ClientId) ($($azAPICallConf['htParameters'].accountType))
"
- }
- else {
- $script:paramsUsed += "ExecutedBy: $($azAPICallConf['checkContext'].Account.Id) ($($azAPICallConf['htParameters'].accountType), $($azAPICallConf['htParameters'].userType))
"
- }
- #$script:paramsUsed += "ManagementGroupId: $($ManagementGroupId)
"
- $script:paramsUsed += 'HierarchyMapOnly: false
'
- Write-Host " Creating HierarchyMap, TenantSummary, DefinitionInsights and ScopeInsights - use parameter: '-HierarchyMapOnly' to only create the HierarchyMap" -ForegroundColor Yellow
-
- if ($azAPICallConf['htParameters'].ManagementGroupsOnly) {
- Write-Host " Management Groups only = $($azAPICallConf['htParameters'].ManagementGroupsOnly)" -ForegroundColor Green
- }
- else {
- Write-Host " Management Groups only = $($azAPICallConf['htParameters'].ManagementGroupsOnly) - use parameter -ManagementGroupsOnly to only collect data for Management Groups" -ForegroundColor Yellow
- }
-
- if (($SubscriptionQuotaIdWhitelist).count -eq 1 -and $SubscriptionQuotaIdWhitelist[0] -eq 'undefined') {
- Write-Host " Subscription Whitelist disabled - use parameter: '-SubscriptionQuotaIdWhitelist' to whitelist QuotaIds" -ForegroundColor Yellow
- $script:paramsUsed += 'SubscriptionQuotaIdWhitelist: false
'
- }
- else {
- Write-Host ' Subscription Whitelist enabled. Azure Governance Visualizer will only process Subscriptions where QuotaId startswith one of the following strings:' -ForegroundColor Green
- foreach ($quotaIdFromSubscriptionQuotaIdWhitelist in $SubscriptionQuotaIdWhitelist) {
- Write-Host " - $($quotaIdFromSubscriptionQuotaIdWhitelist)" -ForegroundColor Green
- }
- foreach ($whiteListEntry in $SubscriptionQuotaIdWhitelist) {
- if ($whiteListEntry -eq 'undefined') {
- Write-Host "When defining the 'SubscriptionQuotaIdWhitelist' make sure to remove the 'undefined' entry from the array :)" -ForegroundColor Red
- Throw 'Error - Azure Governance Visualizer: check the last console output for details'
- }
- }
- $script:paramsUsed += "SubscriptionQuotaIdWhitelist: $($SubscriptionQuotaIdWhitelist -join ', ')
"
- }
-
- if ($azAPICallConf['htParameters'].NoMDfCSecureScore -eq $true) {
- Write-Host " Microsoft Defender for Cloud Secure Score disabled (-NoMDfCSecureScore = $($azAPICallConf['htParameters'].NoMDfCSecureScore))" -ForegroundColor Green
- $script:paramsUsed += 'NoMDfCSecureScore: true
'
- }
- else {
- Write-Host " Microsoft Defender for Cloud Secure Score enabled - use parameter: '-NoMDfCSecureScore' to disable" -ForegroundColor Yellow
- $script:paramsUsed += 'NoMDfCSecureScore: false
'
- }
-
- if ($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData -eq $true) {
- Write-Host " Scrub Identity information for identityType='User' enabled (-DoNotShowRoleAssignmentsUserData = $($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData))" -ForegroundColor Green
- $script:paramsUsed += 'DoNotShowRoleAssignmentsUserData: true
'
- }
- else {
- Write-Host " Scrub Identity information for identityType='User' disabled - use parameter: '-DoNotShowRoleAssignmentsUserData' to scrub information such as displayName and signInName (email) for identityType='User'" -ForegroundColor Yellow
- $script:paramsUsed += 'DoNotShowRoleAssignmentsUserData: false
'
- }
-
- if ($LimitCriticalPercentage -eq 80) {
- Write-Host " ARM Limits warning set to 80% (default) - use parameter: '-LimitCriticalPercentage' to set warning level accordingly" -ForegroundColor Yellow
- #$script:paramsUsed += "LimitCriticalPercentage: 80% (default)
"
- }
- else {
- Write-Host " ARM Limits warning set to $($LimitCriticalPercentage)% (custom)" -ForegroundColor Green
- #$script:paramsUsed += "LimitCriticalPercentage: $($LimitCriticalPercentage)%
"
- }
-
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- Write-Host " Policy States enabled - use parameter: '-NoPolicyComplianceStates' to disable Policy States" -ForegroundColor Yellow
- $script:paramsUsed += 'NoPolicyComplianceStates: false
'
- }
- else {
- Write-Host " Policy States disabled (-NoPolicyComplianceStates = $($azAPICallConf['htParameters'].NoPolicyComplianceStates))" -ForegroundColor Green
- $script:paramsUsed += 'NoPolicyComplianceStates: true
'
- }
-
- if (-not $NoResourceDiagnosticsPolicyLifecycle) {
- Write-Host " Resource Diagnostics Policy Lifecycle recommendations enabled - use parameter: '-NoResourceDiagnosticsPolicyLifecycle' to disable Resource Diagnostics Policy Lifecycle recommendations" -ForegroundColor Yellow
- $script:paramsUsed += 'NoResourceDiagnosticsPolicyLifecycle: false
'
- }
- else {
- Write-Host " Resource Diagnostics Policy Lifecycle disabled (-NoResourceDiagnosticsPolicyLifecycle = $($NoResourceDiagnosticsPolicyLifecycle))" -ForegroundColor Green
- $script:paramsUsed += 'NoResourceDiagnosticsPolicyLifecycle: true
'
- }
-
- if (-not $NoAADGroupsResolveMembers) {
- Write-Host " AAD Groups resolve members enabled (honors parameter -DoNotShowRoleAssignmentsUserData) - use parameter: '-NoAADGroupsResolveMembers' to disable resolving AAD Group memberships" -ForegroundColor Yellow
- $script:paramsUsed += 'NoAADGroupsResolveMembers: false
'
- if ($AADGroupMembersLimit -eq 500) {
- Write-Host " AADGroupMembersLimit = $AADGroupMembersLimit" -ForegroundColor Yellow
- $script:paramsUsed += "AADGroupMembersLimit: $AADGroupMembersLimit
"
- }
- else {
- Write-Host " AADGroupMembersLimit = $AADGroupMembersLimit" -ForegroundColor Green
- $script:paramsUsed += "AADGroupMembersLimit: $AADGroupMembersLimit
"
- }
- }
- else {
- Write-Host " AAD Groups resolve members disabled (-NoAADGroupsResolveMembers = $($NoAADGroupsResolveMembers))" -ForegroundColor Green
- $script:paramsUsed += 'NoAADGroupsResolveMembers: true
'
- }
-
- Write-Host " AADServicePrincipalExpiryWarningDays: $AADServicePrincipalExpiryWarningDays" -ForegroundColor Yellow
- #$script:paramsUsed += "AADServicePrincipalExpiryWarningDays: $AADServicePrincipalExpiryWarningDays
"
-
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- if (-not $AzureConsumptionPeriod -is [int]) {
- Write-Host 'parameter -AzureConsumptionPeriod must be an integer'
- Throw 'Error - Azure Governance Visualizer: check the last console output for details'
- }
- elseif ($AzureConsumptionPeriod -eq 0) {
- Write-Host 'parameter -AzureConsumptionPeriod must be gt 0'
- Throw 'Error - Azure Governance Visualizer: check the last console output for details'
- }
- else {
- #$azureConsumptionStartDate = ((Get-Date).AddDays( - ($($AzureConsumptionPeriod)))).ToString("yyyy-MM-dd")
- #$azureConsumptionEndDate = ((Get-Date).AddDays(-1)).ToString("yyyy-MM-dd")
-
- if ($AzureConsumptionPeriod -eq 1) {
- Write-Host " Azure Consumption reporting enabled: $AzureConsumptionPeriod days (default) ($azureConsumptionStartDate - $azureConsumptionEndDate) - use parameter: '-AzureConsumptionPeriod' to define the period (days)" -ForegroundColor Yellow
- }
- else {
- Write-Host " Azure Consumption reporting enabled: $AzureConsumptionPeriod days ($azureConsumptionStartDate - $azureConsumptionEndDate)" -ForegroundColor Green
- }
-
- if (-not $NoAzureConsumptionReportExportToCSV) {
- Write-Host " Azure Consumption report export to CSV enabled - use parameter: '-NoAzureConsumptionReportExportToCSV' to disable" -ForegroundColor Yellow
- }
- else {
- Write-Host " Azure Consumption report export to CSV disabled (-NoAzureConsumptionReportExportToCSV = $($NoAzureConsumptionReportExportToCSV))" -ForegroundColor Green
- }
- $script:paramsUsed += "DoAzureConsumption: true ($AzureConsumptionPeriod days ($azureConsumptionStartDate - $azureConsumptionEndDate))
"
- $script:paramsUsed += "NoAzureConsumptionReportExportToCSV: $NoAzureConsumptionReportExportToCSV
"
- }
- }
- else {
- Write-Host " Azure Consumption reporting disabled (-DoAzureConsumption = $($azAPICallConf['htParameters'].DoAzureConsumption))" -ForegroundColor Green
- $script:paramsUsed += 'DoAzureConsumption: false
'
- }
-
- if ($NoScopeInsights) {
- Write-Host " ScopeInsights will not be created (-NoScopeInsights = $($NoScopeInsights))" -ForegroundColor Green
- $script:paramsUsed += 'NoScopeInsights: true
'
- }
- else {
- Write-Host " ScopeInsights will be created (-NoScopeInsights = $($NoScopeInsights)) Q: Why would you not want to show ScopeInsights? A: In larger tenants ScopeInsights may blow up the html file (up to unusable due to html file size)" -ForegroundColor Yellow
- $script:paramsUsed += 'NoScopeInsights: false
'
- }
-
- if ($NoSingleSubscriptionOutput) {
- Write-Host " No single Subscription output will not be created (-NoSingleSubscriptionOutput = $($NoSingleSubscriptionOutput))" -ForegroundColor Green
- $script:paramsUsed += 'NoSingleSubscriptionOutput: true
'
- }
- else {
- Write-Host " Single Subscription output will be created (-NoSingleSubscriptionOutput = $($NoSingleSubscriptionOutput))" -ForegroundColor Yellow
- $script:paramsUsed += 'NoSingleSubscriptionOutput: false
'
- }
-
- if ($azAPICallConf['htParameters'].NoResourceProvidersDetailed -eq $true) {
- Write-Host " ResourceProvider Detailed for TenantSummary disabled (-NoResourceProvidersDetailed = $($azAPICallConf['htParameters'].NoResourceProvidersDetailed))" -ForegroundColor Green
- $script:paramsUsed += "NoResourceProvidersDetailed: $($azAPICallConf['htParameters'].NoResourceProvidersDetailed)
"
- }
- else {
- Write-Host " ResourceProvider Detailed for TenantSummary enabled - use parameter: '-NoResourceProvidersDetailed' to disable" -ForegroundColor Yellow
- $script:paramsUsed += "NoResourceProvidersDetailed: $($azAPICallConf['htParameters'].NoResourceProvidersDetailed)
"
- }
-
- if ($azAPICallConf['htParameters'].NoResourceProvidersAtAll -eq $true) {
- Write-Host " ResourceProvider collection disabled (NoResourceProvidersAtAll = $($azAPICallConf['htParameters'].NoResourceProvidersAtAll))" -ForegroundColor Green
- $script:paramsUsed += "NoResourceProvidersAtAll: $($azAPICallConf['htParameters'].NoResourceProvidersAtAll)
"
- }
- else {
- Write-Host " ResourceProvider collection enabled - use parameter: 'NoResourceProvidersAtAll' to disable" -ForegroundColor Yellow
- $script:paramsUsed += "NoResourceProvidersAtAll: $($azAPICallConf['htParameters'].NoResourceProvidersAtAll)
"
- }
-
- if ($azAPICallConf['htParameters'].LargeTenant -or $azAPICallConf['htParameters'].PolicyAtScopeOnly -or $azAPICallConf['htParameters'].RBACAtScopeOnly) {
- if ($azAPICallConf['htParameters'].LargeTenant) {
- Write-Host " TenantSummary Policy assignments and Role assignments will not include assignment information on scopes where assignment is inherited, ScopeInsights will not be created, ResourceProvidersDetailed will not be created (-LargeTenant = $($azAPICallConf['htParameters'].LargeTenant))" -ForegroundColor Green
- $script:paramsUsed += "LargeTenant: $($azAPICallConf['htParameters'].LargeTenant)
"
- $script:paramsUsed += "LargeTenant -> PolicyAtScopeOnly: $($azAPICallConf['htParameters'].PolicyAtScopeOnly)
"
- $script:paramsUsed += "LargeTenant -> RBACAtScopeOnly: $($azAPICallConf['htParameters'].RBACAtScopeOnly)
"
- $script:paramsUsed += "LargeTenant -> NoScopeInsights: $($NoScopeInsights)
"
- $script:paramsUsed += "LargeTenant -> NoResourceProvidersDetailed: $($azAPICallConf['htParameters'].NoResourceProvidersDetailed)
"
- }
- else {
- Write-Host " TenantSummary LargeTenant disabled (-LargeTenant = $($azAPICallConf['htParameters'].LargeTenant)) Q: Why would you not want to enable -LargeTenant? A: In larger tenants showing the inheritance on each scope may blow up the html file (up to unusable due to html file size)" -ForegroundColor Yellow
- $script:paramsUsed += "LargeTenant: $($azAPICallConf['htParameters'].LargeTenant)
"
-
- if ($azAPICallConf['htParameters'].PolicyAtScopeOnly) {
- Write-Host " TenantSummary Policy assignments will not include assignment information on scopes where assignment is inherited (PolicyAtScopeOnly = $($azAPICallConf['htParameters'].PolicyAtScopeOnly))" -ForegroundColor Green
- $script:paramsUsed += "PolicyAtScopeOnly: $($azAPICallConf['htParameters'].PolicyAtScopeOnly)
"
- }
- else {
- Write-Host " TenantSummary Policy assignments will include assignment information on scopes where assignment is inherited (PolicyAtScopeOnly = $($azAPICallConf['htParameters'].PolicyAtScopeOnly))" -ForegroundColor Yellow
- $script:paramsUsed += "PolicyAtScopeOnly: $($azAPICallConf['htParameters'].PolicyAtScopeOnly)
"
- }
-
- if ($azAPICallConf['htParameters'].RBACAtScopeOnly) {
- Write-Host " TenantSummary Role assignments will not include assignment information on scopes where assignment is inherited (RBACAtScopeOnly = $($azAPICallConf['htParameters'].RBACAtScopeOnly))" -ForegroundColor Green
- $script:paramsUsed += "RBACAtScopeOnly: $($azAPICallConf['htParameters'].RBACAtScopeOnly)
"
- }
- else {
- Write-Host " TenantSummary Role assignments will include assignment information on scopes where assignment is inherited (RBACAtScopeOnly = $($azAPICallConf['htParameters'].RBACAtScopeOnly))" -ForegroundColor Yellow
- $script:paramsUsed += "RBACAtScopeOnly: $($azAPICallConf['htParameters'].RBACAtScopeOnly)
"
- }
- }
- }
- else {
- Write-Host " TenantSummary LargeTenant disabled (-LargeTenant = $($azAPICallConf['htParameters'].LargeTenant)) Q: Why would you not want to enable -LargeTenant? A: In larger tenants showing the inheritance on each scope may blow up the html file (up to unusable due to html file size)" -ForegroundColor Yellow
- $script:paramsUsed += "LargeTenant: $($azAPICallConf['htParameters'].LargeTenant)
"
-
- if ($azAPICallConf['htParameters'].PolicyAtScopeOnly) {
- Write-Host " TenantSummary Policy assignments will not include assignment information on scopes where assignment is inherited (PolicyAtScopeOnly = $($azAPICallConf['htParameters'].PolicyAtScopeOnly))" -ForegroundColor Green
- $script:paramsUsed += "PolicyAtScopeOnly: $($azAPICallConf['htParameters'].PolicyAtScopeOnly)
"
- }
- else {
- Write-Host " TenantSummary Policy assignments will include assignment information on scopes where assignment is inherited (PolicyAtScopeOnly = $($azAPICallConf['htParameters'].PolicyAtScopeOnly))" -ForegroundColor Yellow
- $script:paramsUsed += "PolicyAtScopeOnly: $($azAPICallConf['htParameters'].PolicyAtScopeOnly)
"
- }
-
- if ($azAPICallConf['htParameters'].RBACAtScopeOnly) {
- Write-Host " TenantSummary Role assignments will not include assignment information on scopes where assignment is inherited (RBACAtScopeOnly = $($azAPICallConf['htParameters'].RBACAtScopeOnly))" -ForegroundColor Green
- $script:paramsUsed += "RBACAtScopeOnly: $($azAPICallConf['htParameters'].RBACAtScopeOnly)
"
- }
- else {
- Write-Host " TenantSummary Role assignments will include assignment information on scopes where assignment is inherited (RBACAtScopeOnly = $($azAPICallConf['htParameters'].RBACAtScopeOnly))" -ForegroundColor Yellow
- $script:paramsUsed += "RBACAtScopeOnly: $($azAPICallConf['htParameters'].RBACAtScopeOnly)
"
- }
- }
-
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- Write-Host " TenantSummary Policy assignments will also include assignments on ResourceGroups (DoNotIncludeResourceGroupsOnPolicy = $($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy))" -ForegroundColor Yellow
- $script:paramsUsed += 'DoNotIncludeResourceGroupsOnPolicy: false
'
- }
- else {
- Write-Host " TenantSummary Policy assignments will not include assignments on ResourceGroups (DoNotIncludeResourceGroupsOnPolicy = $($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy))" -ForegroundColor Green
- $script:paramsUsed += 'DoNotIncludeResourceGroupsOnPolicy: true
'
- }
-
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- Write-Host " TenantSummary RBAC Role assignments will also include assignments on ResourceGroups and Resources (DoNotIncludeResourceGroupsAndResourcesOnRBAC = $($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC))" -ForegroundColor Yellow
- $script:paramsUsed += 'DoNotIncludeResourceGroupsAndResourcesOnRBAC: false
'
- }
- else {
- Write-Host " TenantSummary RBAC Role assignments will not include assignments on ResourceGroups and Resources (DoNotIncludeResourceGroupsAndResourcesOnRBAC = $($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC))" -ForegroundColor Green
- $script:paramsUsed += 'DoNotIncludeResourceGroupsAndResourcesOnRBAC: true
'
- }
-
- if (-not $NoCsvExport) {
- Write-Host " CSV Export enabled: enriched 'Role assignments' data, enriched 'Policy assignments' data and 'all resources' (subscriptionId, mgPath, resourceType, id, name, location, tags, createdTime, changedTime) (-NoCsvExport = $($NoCsvExport))" -ForegroundColor Yellow
- $script:paramsUsed += 'NoCsvExport: false
'
- }
- else {
- Write-Host " CSV Export disabled: enriched 'Role assignments' data, enriched 'Policy assignments' data and 'all resources' (subscriptionId, mgPath, resourceType, id, name, location, tags, createdTime, changedTime) (-NoCsvExport = $($NoCsvExport))" -ForegroundColor Green
- $script:paramsUsed += 'NoCsvExport: true
'
- }
-
- if (-not $azAPICallConf['htParameters'].NoJsonExport) {
- Write-Host " JSON Export enabled: export of ManagementGroup Hierarchy including all MG/Sub Policy/RBAC definitions, Policy/RBAC assignments and some more relevant information to JSON (-NoJsonExport = $($azAPICallConf['htParameters'].NoJsonExport))" -ForegroundColor Yellow
- $script:paramsUsed += 'NoJsonExport: false
'
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
- if (-not $JsonExportExcludeResourceGroups) {
- Write-Host " JSON Export will also include Policy assignments on ResourceGroups (JsonExportExcludeResourceGroups = $($JsonExportExcludeResourceGroups))" -ForegroundColor Yellow
- $script:paramsUsed += "JsonExportExcludeResourceGroups Policy: $($JsonExportExcludeResourceGroups)
"
- }
- else {
- Write-Host " JSON Export will not include Policy assignments on ResourceGroups (JsonExportExcludeResourceGroups = $($JsonExportExcludeResourceGroups))" -ForegroundColor Green
- $script:paramsUsed += "JsonExportExcludeResourceGroups Policy: $($JsonExportExcludeResourceGroups)
"
- }
- }
- if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
- if (-not $JsonExportExcludeResourceGroups) {
- Write-Host " JSON Export will also include Role assignments on ResourceGroups (JsonExportExcludeResourceGroups = $($JsonExportExcludeResourceGroups))" -ForegroundColor Yellow
- $script:paramsUsed += "JsonExportExcludeResourceGroups RBAC: $($JsonExportExcludeResourceGroups)
"
-
- }
- else {
- Write-Host " JSON Export will not include Role assignments on ResourceGroups (JsonExportExcludeResourceGroups = $($JsonExportExcludeResourceGroups))" -ForegroundColor Green
- $script:paramsUsed += "JsonExportExcludeResourceGroups RBAC: $($JsonExportExcludeResourceGroups)
"
- }
- if (-not $JsonExportExcludeResources) {
- Write-Host " JSON Export will also include Role assignments on Resources (JsonExportExcludeResources = $($JsonExportExcludeResources))" -ForegroundColor Yellow
- $script:paramsUsed += "JsonExportExcludeResources RBAC: $($JsonExportExcludeResources)
"
- }
- else {
- Write-Host " JSON Export will not include Role assignments on Resources (JsonExportExcludeResources = $($JsonExportExcludeResources))" -ForegroundColor Green
- $script:paramsUsed += "JsonExportExcludeResources RBAC: $($JsonExportExcludeResources)
"
- }
- }
- }
- else {
- Write-Host " JSON Export disabled: export of ManagementGroup Hierarchy including all MG/Sub Policy/RBAC definitions, Policy/RBAC assignments and some more relevant information to JSON (-NoJsonExport = $($azAPICallConf['htParameters'].NoJsonExport))" -ForegroundColor Green
- $script:paramsUsed += 'NoJsonExport: true
'
- }
-
- if ($ThrottleLimit -eq 10) {
- Write-Host " ThrottleLimit = $ThrottleLimit" -ForegroundColor Yellow
- #$script:paramsUsed += "ThrottleLimit: $ThrottleLimit
"
- }
- else {
- Write-Host " ThrottleLimit = $ThrottleLimit" -ForegroundColor Green
- #$script:paramsUsed += "ThrottleLimit: $ThrottleLimit
"
- }
-
-
- if ($ChangeTrackingDays -eq 14) {
- Write-Host " ChangeTrackingDays = $ChangeTrackingDays" -ForegroundColor Yellow
- #$script:paramsUsed += "ChangeTrackingDays: $ChangeTrackingDays
"
- }
- else {
- Write-Host " ChangeTrackingDays = $ChangeTrackingDays" -ForegroundColor Green
- #$script:paramsUsed += "ChangeTrackingDays: $ChangeTrackingDays
"
- }
-
- if ($azAPICallConf['htParameters'].NoResources) {
- Write-Host " NoResources = $($azAPICallConf['htParameters'].NoResources)" -ForegroundColor Green
- $script:paramsUsed += "NoResources: $($azAPICallConf['htParameters'].NoResources)
"
- }
- else {
- Write-Host " NoResources = $($azAPICallConf['htParameters'].NoResources)" -ForegroundColor Yellow
- $script:paramsUsed += "NoResources: $($azAPICallConf['htParameters'].NoResources)
"
- }
-
- if ($ShowMemoryUsage) {
- Write-Host " ShowMemoryUsage = $($ShowMemoryUsage)" -ForegroundColor Green
- #$script:paramsUsed += "ShowMemoryUsage: $($ShowMemoryUsage)
"
- }
- else {
- Write-Host " ShowMemoryUsage = $($ShowMemoryUsage)" -ForegroundColor Yellow
- #$script:paramsUsed += "ShowMemoryUsage: $($ShowMemoryUsage)
"
- }
-
- if ($CriticalMemoryUsage -ne 99) {
- Write-Host " CriticalMemoryUsage = $($CriticalMemoryUsage)%" -ForegroundColor green
- #$script:paramsUsed += "ShowMemoryUsage: $($ShowMemoryUsage)
"
- }
- else {
- Write-Host " CriticalMemoryUsage = $($CriticalMemoryUsage)%" -ForegroundColor Yellow
- #$script:paramsUsed += "ShowMemoryUsage: $($ShowMemoryUsage)
"
- }
-
- if ($azAPICallConf['htParameters'].DoPSRule) {
- Write-Host " DoPSRule = $($azAPICallConf['htParameters'].DoPSRule)" -ForegroundColor Green
- $script:paramsUsed += "DoPSRule: $($azAPICallConf['htParameters'].DoPSRule)
"
-
- if ($azAPICallConf['htParameters'].PSRuleFailedOnly) {
- Write-Host " PSRuleFailedOnly = $($azAPICallConf['htParameters'].PSRuleFailedOnly)" -ForegroundColor Green
- $script:paramsUsed += "PSRuleFailedOnly: $($azAPICallConf['htParameters'].PSRuleFailedOnly)
"
- }
- else {
- Write-Host " PSRuleFailedOnly = $($azAPICallConf['htParameters'].PSRuleFailedOnly)" -ForegroundColor Yellow
- $script:paramsUsed += "PSRuleFailedOnly: $($azAPICallConf['htParameters'].PSRuleFailedOnly)
"
- }
- }
- else {
- Write-Host " DoPSRule = $($azAPICallConf['htParameters'].DoPSRule)" -ForegroundColor Yellow
- $script:paramsUsed += "DoPSRule: $($azAPICallConf['htParameters'].DoPSRule)
"
- }
-
- if ($NoPIMEligibility) {
- Write-Host " NoPIMEligibility = $($NoPIMEligibility)" -ForegroundColor Green
- $script:paramsUsed += "NoPIMEligibility: $($NoPIMEligibility)
"
- }
- else {
- Write-Host " NoPIMEligibility = $($NoPIMEligibility)" -ForegroundColor Yellow
- $script:paramsUsed += "NoPIMEligibility: $($NoPIMEligibility)
"
- }
-
- if ($PIMEligibilityIgnoreScope) {
- Write-Host " PIMEligibilityIgnoreScope = $($PIMEligibilityIgnoreScope)" -ForegroundColor Green
- #$script:paramsUsed += "PIMEligibilityIgnoreScope: $($PIMEligibilityIgnoreScope)
"
- }
- else {
- Write-Host " PIMEligibilityIgnoreScope = $($PIMEligibilityIgnoreScope)" -ForegroundColor Yellow
- #$script:paramsUsed += "PIMEligibilityIgnoreScope: $($PIMEligibilityIgnoreScope)
"
- }
-
- if ($NoPIMEligibilityIntegrationRoleAssignmentsAll) {
- Write-Host " NoPIMEligibilityIntegrationRoleAssignmentsAll = $($NoPIMEligibilityIntegrationRoleAssignmentsAll)" -ForegroundColor Green
- #$script:paramsUsed += "NoPIMEligibilityIntegrationRoleAssignmentsAll: $($NoPIMEligibilityIntegrationRoleAssignmentsAll)
"
- }
- else {
- Write-Host " NoPIMEligibilityIntegrationRoleAssignmentsAll = $($NoPIMEligibilityIntegrationRoleAssignmentsAll)" -ForegroundColor Yellow
- #$script:paramsUsed += "NoPIMEligibilityIntegrationRoleAssignmentsAll: $($NoPIMEligibilityIntegrationRoleAssignmentsAll)
"
- }
-
- if ($NoDefinitionInsightsDedicatedHTML) {
- Write-Host " NoDefinitionInsightsDedicatedHTML = $($NoDefinitionInsightsDedicatedHTML)" -ForegroundColor Green
- #$script:paramsUsed += "NoDefinitionInsightsDedicatedHTML: $($NoDefinitionInsightsDedicatedHTML)
"
- }
- else {
- Write-Host " NoDefinitionInsightsDedicatedHTML = $($NoDefinitionInsightsDedicatedHTML)" -ForegroundColor Yellow
- #$script:paramsUsed += "NoDefinitionInsightsDedicatedHTML: $($NoDefinitionInsightsDedicatedHTML)
"
- }
-
- if ($NoALZPolicyVersionChecker) {
- Write-Host " NoALZPolicyVersionChecker = $($NoALZPolicyVersionChecker)" -ForegroundColor Green
- #$script:paramsUsed += "NoALZPolicyVersionChecker: $($NoALZPolicyVersionChecker)
"
- }
- else {
- Write-Host " NoALZPolicyVersionChecker = $($NoALZPolicyVersionChecker)" -ForegroundColor Yellow
- #$script:paramsUsed += "NoALZPolicyVersionChecker: $($NoALZPolicyVersionChecker)
"
- }
-
- if ($NoStorageAccountAccessAnalysis) {
- Write-Host " NoStorageAccountAccessAnalysis = $($NoStorageAccountAccessAnalysis)" -ForegroundColor Green
- #$script:paramsUsed += "NoStorageAccountAccessAnalysis: $($NoStorageAccountAccessAnalysis)
"
- }
- else {
- Write-Host " NoStorageAccountAccessAnalysis = $($NoStorageAccountAccessAnalysis)" -ForegroundColor Yellow
- #$script:paramsUsed += "NoStorageAccountAccessAnalysis: $($NoStorageAccountAccessAnalysis)
"
- if ($StorageAccountAccessAnalysisSubscriptionTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisSubscriptionTags.Count -gt 0) {
- Write-Host " StorageAccountAccessAnalysisSubscriptionTags: $($StorageAccountAccessAnalysisSubscriptionTags -join ', ')" -ForegroundColor Green
- }
- if ($StorageAccountAccessAnalysisStorageAccountTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisStorageAccountTags.Count -gt 0) {
- Write-Host " StorageAccountAccessAnalysisStorageAccountTags: $($StorageAccountAccessAnalysisStorageAccountTags -join ', ')" -ForegroundColor Green
- }
- }
-
- if ($NoNetwork) {
- Write-Host " NoNetwork = $($NoNetwork)" -ForegroundColor Green
- #$script:paramsUsed += "NoNetwork: $($NoNetwork)
"
- }
- else {
- Write-Host " NoNetwork = $($NoNetwork)" -ForegroundColor Yellow
- #$script:paramsUsed += "NoNetwork: $($NoNetwork)
"
-
- if ($NetworkSubnetIPAddressUsageCriticalPercentage -ne 80) {
- Write-Host " NetworkSubnetIPAddressUsageCriticalPercentage = $($NetworkSubnetIPAddressUsageCriticalPercentage)" -ForegroundColor Green
- #$script:paramsUsed += "NetworkSubnetIPAddressUsageCriticalPercentage: $($NetworkSubnetIPAddressUsageCriticalPercentage)
"
- }
- else {
- Write-Host " NoNetwork = $($NetworkSubnetIPAddressUsageCriticalPercentage)" -ForegroundColor Yellow
- #$script:paramsUsed += "NetworkSubnetIPAddressUsageCriticalPercentage: $($NetworkSubnetIPAddressUsageCriticalPercentage)
"
- }
- }
-
- if ($GitHubActionsOIDC) {
- Write-Host " GitHubActionsOIDC = $($GitHubActionsOIDC)" -ForegroundColor Green
- #$script:paramsUsed += "GitHubActionsOIDC: $($GitHubActionsOIDC)
"
- }
- else {
- Write-Host " GitHubActionsOIDC = $($GitHubActionsOIDC)" -ForegroundColor Yellow
- #$script:paramsUsed += "GitHubActionsOIDC: $($GitHubActionsOIDC)
"
- }
-
- }
- #endregion RunInfo
-}
-function selectMg() {
- Write-Host 'Please select a Management Group from the list below:'
- $MgtGroupArray | Select-Object '#', Name, @{Name = 'displayName'; Expression = { $_.properties.displayName } }, Id | Format-Table
- Write-Host "If you don't see your ManagementGroupID try using the parameter -ManagementGroupID" -ForegroundColor Yellow
- if ($msg) {
- Write-Host $msg -ForegroundColor Red
- }
-
- $script:SelectedMG = Read-Host "Please enter a selection from 1 to $(($MgtGroupArray).count)"
-
- if ($SelectedMG -match '^[\d\.]+$') {
- if ([int]$SelectedMG -lt 1 -or [int]$SelectedMG -gt ($MgtGroupArray).count) {
- $msg = "last input '$SelectedMG' is out of range, enter a number from the selection!"
- selectMg
- }
- }
- else {
- $msg = "last input '$SelectedMG' is not numeric, enter a number from the selection!"
- selectMg
- }
-}
-function setBaseVariablesMG {
- if (($azAPICallConf['checkContext']).Tenant.Id -ne $ManagementGroupId) {
- $script:mgSubPathTopMg = $selectedManagementGroupId.ParentName
- $script:getMgParentId = $selectedManagementGroupId.ParentName
- $script:getMgParentName = $selectedManagementGroupId.ParentDisplayName
- $script:mermaidprnts = "'$(($azAPICallConf['checkContext']).Tenant.Id)',$getMgParentId"
- }
- else {
- $script:hierarchyLevel = -1
- $script:mgSubPathTopMg = "$ManagementGroupId"
- $script:getMgParentId = "'$ManagementGroupId'"
- $script:getMgParentName = 'Tenant Root'
- $script:mermaidprnts = "'$getMgParentId',$getMgParentId"
- }
-}
-function setOutput {
- if (-not [IO.Path]::IsPathRooted($outputPath)) {
- $outputPath = Join-Path -Path (Get-Location).Path -ChildPath $outputPath
- }
- $outputPath = Join-Path -Path $outputPath -ChildPath '.'
- $script:outputPath = [IO.Path]::GetFullPath($outputPath)
- if (-not (Test-Path $outputPath)) {
- Write-Host "path $outputPath does not exist - please create it!" -ForegroundColor Red
- Throw 'Error - check the last console output for details'
- }
- else {
- Write-Host "Output/Files will be created in path '$outputPath'"
- }
-
- #fileTimestamp
- try {
- $script:fileTimestamp = (Get-Date -Format $FileTimeStampFormat)
- }
- catch {
- Write-Host "fileTimestamp format: '$($FileTimeStampFormat)' invalid; continue with default format: 'yyyyMMdd_HHmmss'" -ForegroundColor Red
- $FileTimeStampFormat = 'yyyyMMdd_HHmmss'
- $script:fileTimestamp = (Get-Date -Format $FileTimeStampFormat)
- }
-
- $script:executionDateTimeInternationalReadable = Get-Date -Format 'dd-MMM-yyyy HH:mm:ss'
- $script:currentTimeZone = (Get-TimeZone).Id
-}
-function setTranscript {
- if ($ManagementGroupId) {
- if ($onAzureDevOpsOrGitHubActions -eq $true) {
- if ($HierarchyMapOnly -eq $true) {
- $script:fileNameTranscript = "AzGovViz_HierarchyMapOnly_$($ManagementGroupId)_Log.txt"
- }
- elseif ($ManagementGroupsOnly -eq $true) {
- $script:fileNameTranscript = "AzGovViz_ManagementGroupsOnly_$($ManagementGroupId)_Log.txt"
- }
- else {
- $script:fileNameTranscript = "AzGovViz_$($ManagementGroupId)_Log.txt"
- }
- }
- else {
- if ($HierarchyMapOnly -eq $true) {
- $script:fileNameTranscript = "AzGovViz_HierarchyMapOnly_$($ProductVersion)_$($fileTimestamp)_$($ManagementGroupId)_Log.txt"
- }
- elseif ($ManagementGroupsOnly -eq $true) {
- $script:fileNameTranscript = "AzGovViz_ManagementGroupsOnly_$($ProductVersion)_$($fileTimestamp)_$($ManagementGroupId)_Log.txt"
- }
- else {
- $script:fileNameTranscript = "AzGovViz_$($ProductVersion)_$($fileTimestamp)_$($ManagementGroupId)_Log.txt"
- }
- }
- }
- else {
- if ($onAzureDevOpsOrGitHubActions -eq $true) {
- if ($HierarchyMapOnly -eq $true) {
- $script:fileNameTranscript = 'AzGovViz_HierarchyMapOnly_Log.txt'
- }
- elseif ($ManagementGroupsOnly -eq $true) {
- $script:fileNameTranscript = 'AzGovViz_ManagementGroupsOnly_Log.txt'
- }
- else {
- $script:fileNameTranscript = 'AzGovViz_Log.txt'
- }
- }
- else {
- if ($HierarchyMapOnly -eq $true) {
- $script:fileNameTranscript = "AzGovViz_HierarchyMapOnly_$($ProductVersion)_$($fileTimestamp)_Log.txt"
- }
- elseif ($ManagementGroupsOnly -eq $true) {
- $script:fileNameTranscript = "AzGovViz_ManagementGroupsOnly_$($ProductVersion)_$($fileTimestamp)_Log.txt"
- }
- else {
- $script:fileNameTranscript = "AzGovViz_$($ProductVersion)_$($fileTimestamp)_Log.txt"
- }
- }
- }
- Write-Host "Writing transcript: $($outputPath)$($DirectorySeparatorChar)$($fileNameTranscript)"
- Start-Transcript -Path "$($outputPath)$($DirectorySeparatorChar)$($fileNameTranscript)"
-}
-function showMemoryUsage {
-
- function makeDouble {
- [CmdletBinding()]
- Param
- (
- [Parameter(Mandatory = $true)]$MemoryUsed
- )
-
- try {
- $memoryUsedDouble = [double]($memoryUsed -replace ',', '.')
- }
- catch {
- $memoryUsedDouble = [string]$MemoryUsed
- }
- return $memoryUsedDouble
- }
-
- function getMemoryUsage {
- if ($IsLinux) {
- $memoryUsed = 100 - (free | grep Mem | awk '{print $4/$2 * 100.0}')
- makeDouble $memoryUsed
- }
- if ($IsWindows) {
- $memoryUsed = (Get-CimInstance win32_operatingsystem | ForEach-Object { '{0:N2}' -f ((($_.TotalVisibleMemorySize - $_.FreePhysicalMemory) * 100) / $_.TotalVisibleMemorySize) })
- makeDouble $memoryUsed
- }
- }
- $memoryUsed = getMemoryUsage
-
- if ($memoryUsed -is [double]) {
- if ($memoryUsed -gt $CriticalMemoryUsage) {
- Write-Host "System memory utilization HIGH: $([math]::Round($memoryUsed))%" -ForegroundColor Magenta
- Write-Host 'Init garbage collection (GC)'
- $PSMemoryBefore = [System.GC]::GetTotalMemory($false)
- Write-Host " PS memory used before GC: $($PSMemoryBefore /1MB)MB ($PSMemoryBefore)"
- $startGC = Get-Date
- $PSMemoryAfter = [System.GC]::GetTotalMemory($true)
- $endGC = Get-Date
- $PSMemoryDiff = $PSMemoryBefore - $PSMemoryAfter
- Write-Host " PS memory used after GC: $($PSMemoryAfter /1MB)MB ($PSMemoryAfter)"
- Write-Host " GC cleared $($PSMemoryDiff /1MB)MB ($PSMemoryDiff)" -ForegroundColor Green
- Write-Host " GC duration: $((New-TimeSpan -Start $startGC -End $endGC).TotalSeconds) seconds"
- Write-Host " System memory utilization after GC: $(getMemoryUsage)%"
- }
- else {
- if ($ShowMemoryUsage) {
- Write-Host "System memory utilization: $([math]::Round($memoryUsed))%"
- }
- }
- }
- else {
- Write-Host "System memory utilization: $($memoryUsed)% (not double)"
- }
-}
-function stats {
- #region Stats
- if (-not $StatsOptOut) {
-
- $dur = 0
- if ($durationProduct.TotalMinutes -lt 5) {
- $dur = 5
- }
-
- if ($azAPICallConf['htParameters'].onAzureDevOps) {
- if ($env:BUILD_REPOSITORY_ID) {
- $hashTenantIdOrRepositoryId = [string]($env:BUILD_REPOSITORY_ID)
- }
- else {
- $hashTenantIdOrRepositoryId = [string]($azAPICallConf['checkContext'].Tenant.Id)
- }
- }
- else {
- $hashTenantIdOrRepositoryId = [string]($azAPICallConf['checkContext'].Tenant.Id)
- }
-
- $hashAccId = [string]($azAPICallConf['checkContext'].Account.Id)
-
- $hasher384 = [System.Security.Cryptography.HashAlgorithm]::Create('sha384')
- $hasher512 = [System.Security.Cryptography.HashAlgorithm]::Create('sha512')
-
- $hashTenantIdOrRepositoryIdSplit = $hashTenantIdOrRepositoryId.split('-')
- $hashAccIdSplit = $hashAccId.split('-')
-
- if (($hashTenantIdOrRepositoryIdSplit[0])[0] -match '[a-z]') {
- $hashTenantIdOrRepositoryIdUse = "$(($hashTenantIdOrRepositoryIdSplit[0]).substring(2))$($hashAccIdSplit[2])"
- $hashTenantIdOrRepositoryIdUse = $hasher512.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($hashTenantIdOrRepositoryIdUse))
- $hashTenantIdOrRepositoryIdUse = "$(([System.BitConverter]::ToString($hashTenantIdOrRepositoryIdUse)) -replace '-')"
- }
- else {
- $hashTenantIdOrRepositoryIdUse = "$(($hashTenantIdOrRepositoryIdSplit[4]).substring(6))$($hashAccIdSplit[1])"
- $hashTenantIdOrRepositoryIdUse = $hasher384.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($hashTenantIdOrRepositoryIdUse))
- $hashTenantIdOrRepositoryIdUse = "$(([System.BitConverter]::ToString($hashTenantIdOrRepositoryIdUse)) -replace '-')"
- }
-
- if (($hashAccIdSplit[0])[0] -match '[a-z]') {
- $hashAccIdUse = "$($hashAccIdSplit[0].substring(2))$($hashTenantIdOrRepositoryIdSplit[2])"
- $hashAccIdUse = $hasher512.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($hashAccIdUse))
- $hashAccIdUse = "$(([System.BitConverter]::ToString($hashAccIdUse)) -replace '-')"
- $hashUse = "$($hashAccIdUse)$($hashTenantIdOrRepositoryIdUse)"
- }
- else {
- $hashAccIdUse = "$($hashAccIdSplit[4].substring(6))$($hashTenantIdOrRepositoryIdSplit[1])"
- $hashAccIdUse = $hasher384.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($hashAccIdUse))
- $hashAccIdUse = "$(([System.BitConverter]::ToString($hashAccIdUse)) -replace '-')"
- $hashUse = "$($hashTenantIdOrRepositoryIdUse)$($hashAccIdUse)"
- }
-
- $identifierBase = $hasher512.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($hashUse))
- $script:statsIdentifier = "$(([System.BitConverter]::ToString($identifierBase)) -replace '-')"
-
- $accountInfo = "$($azAPICallConf['htParameters'].accountType)$($azAPICallConf['htParameters'].userType)"
- if ($azAPICallConf['htParameters'].accountType -eq 'ServicePrincipal' -or $azAPICallConf['htParameters'].accountType -eq 'ManagedService' -or $azAPICallConf['htParameters'].accountType -eq 'ClientAssertion') {
- $accountInfo = $azAPICallConf['htParameters'].accountType
- }
-
- $scopeUsage = 'childManagementGroup'
- if ($ManagementGroupId -eq $azAPICallConf['checkContext'].Tenant.Id) {
- $scopeUsage = 'rootManagementGroup'
- }
-
- $statsCountSubscriptions = 'less than 100'
- if (($htSubscriptionsMgPath.Keys).Count -ge 100) {
- $statsCountSubscriptions = 'more than 100'
- }
-
- $tryCounter = 0
- do {
- if ($tryCounter -gt 0) {
- Start-Sleep -Seconds ($tryCounter * 3)
- }
- $tryCounter++
- $statsSuccess = $true
- try {
- $statusBody = @"
-{
- "name": "Microsoft.ApplicationInsights.Event",
- "time": "$((Get-Date).ToUniversalTime())",
- "iKey": "ffcd6b2e-1a5e-429f-9495-e3492decfe06",
- "data": {
- "baseType": "EventData",
- "baseData": {
- "name": "$($Product)",
- "ver": 2,
- "properties": {
- "accType": "$($accountInfo)",
- "azCloud": "$($azAPICallConf['checkContext'].Environment.Name)",
- "identifier": "$($statsIdentifier)",
- "platform": "$($azAPICallConf['htParameters'].CodeRunPlatform)",
- "productVersion": "$($ProductVersion)",
- "AzAPICallVersion": "$($AzAPICallVersion)",
- "psAzAccountsVersion": "$($azAPICallConf['htParameters'].AzAccountsVersion)",
- "psVersion": "$($PSVersionTable.PSVersion)",
- "scopeUsage": "$($scopeUsage)",
- "statsCountErrors": "$($Error.Count)",
- "statsCountSubscriptions": "$($statsCountSubscriptions)",
- "statsParametersDoNotIncludeResourceGroupsAndResourcesOnRBAC": "$($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC)",
- "statsParametersDoNotIncludeResourceGroupsOnPolicy": "$($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy)",
- "statsParametersDoNotShowRoleAssignmentsUserData": "$($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData)",
- "statsParametersHierarchyMapOnly": "$HierarchyMapOnly",
- "statsParametersManagementGroupsOnly": "$($azAPICallConf['htParameters'].ManagementGroupsOnly)",
- "statsParametersLargeTenant": "$($azAPICallConf['htParameters'].LargeTenant)",
- "statsParametersNoASCSecureScore": "$($azAPICallConf['htParameters'].NoMDfCSecureScore)",
- "statsParametersDoAzureConsumption": "$($azAPICallConf['htParameters'].DoAzureConsumption)",
- "statsParametersNoJsonExport": "$($azAPICallConf['htParameters'].NoJsonExport)",
- "statsParametersNoScopeInsights": "$($NoScopeInsights)",
- "statsParametersNoSingleSubscriptionOutput": "$($NoSingleSubscriptionOutput)",
- "statsParametersNoPolicyComplianceStates": "$($azAPICallConf['htParameters'].NoPolicyComplianceStates)",
- "statsParametersNoResourceProvidersDetailed": "$($azAPICallConf['htParameters'].NoResourceProvidersDetailed)",
- "statsParametersNoResourceProvidersAtAll": "$($azAPICallConf['htParameters'].NoResourceProvidersAtAll)",
- "statsParametersNoResources": "$($azAPICallConf['htParameters'].NoResources)",
- "statsParametersPolicyAtScopeOnly": "$($azAPICallConf['htParameters'].PolicyAtScopeOnly)",
- "statsParametersRBACAtScopeOnly": "$($azAPICallConf['htParameters'].RBACAtScopeOnly)",
- "statsParametersDoPSRule": "$($azAPICallConf['htParameters'].DoPSRule)",
- "statsParametersNoPIMEligibility": "$($NoPIMEligibility)",
- "statsParametersNoALZPolicyVersionChecker": "$($NoALZPolicyVersionChecker)",
- "statsParametersNoStorageAccountAccessAnalysis": "$($NoStorageAccountAccessAnalysis)",
- "statsParametersNoNetwork": "$($NoNetwork)",
- "statsTry": "$($tryCounter)",
- "statsDurationProduct": "$($dur)"
- }
- }
- }
-}
-"@
- $stats = Invoke-WebRequest -Uri 'https://dc.services.visualstudio.com/v2/track' -Method 'POST' -Body $statusBody
- }
- catch {
- $statsSuccess = $false
- }
- }
- until($statsSuccess -eq $true -or $tryCounter -gt 5)
- }
- else {
- #noStats
- $script:statsIdentifier = (New-Guid).Guid
- $tryCounter = 0
- do {
- if ($tryCounter -gt 0) {
- Start-Sleep -Seconds ($tryCounter * 3)
- }
- $tryCounter++
- $statsSuccess = $true
- try {
- $statusBody = @"
-{
- "name": "Microsoft.ApplicationInsights.Event",
- "time": "$((Get-Date).ToUniversalTime())",
- "iKey": "ffcd6b2e-1a5e-429f-9495-e3492decfe06",
- "data": {
- "baseType": "EventData",
- "baseData": {
- "name": "$($Product)",
- "ver": 2,
- "properties": {
- "identifier": "$($statsIdentifier)",
- "statsTry": "$($tryCounter)"
- }
- }
- }
-}
-"@
- $stats = Invoke-WebRequest -Uri 'https://dc.services.visualstudio.com/v2/track' -Method 'POST' -Body $statusBody
- }
- catch {
- $statsSuccess = $false
- }
- }
- until($statsSuccess -eq $true -or $tryCounter -gt 5)
- }
- #endregion Stats
-}
-function testGuid {
- [OutputType([bool])]
- param
- (
- [Parameter(Mandatory = $true)]
- [string]$StringGuid
- )
-
- $ObjectGuid = [System.Guid]::empty
- return [System.Guid]::TryParse($StringGuid, [System.Management.Automation.PSReference]$ObjectGuid) # Returns True if successfully parsed
-}
-function testPowerShellVersion {
-
- Write-Host 'Checking PowerShell edition and version'
- $requiredPSVersion = '7.0.3'
- $splitRequiredPSVersion = $requiredPSVersion.split('.')
- $splitRequiredPSVersionMajor = $splitRequiredPSVersion[0]
- $splitRequiredPSVersionMinor = $splitRequiredPSVersion[1]
- $splitRequiredPSVersionPatch = $splitRequiredPSVersion[2]
-
- $thisPSVersion = ($PSVersionTable.PSVersion)
- $thisPSVersionMajor = ($thisPSVersion).Major
- $thisPSVersionMinor = ($thisPSVersion).Minor
- $thisPSVersionPatch = ($thisPSVersion).Patch
-
- $psVersionCheckResult = 'letsCheck'
-
- if ($PSVersionTable.PSEdition -eq 'Core' -and $thisPSVersionMajor -eq $splitRequiredPSVersionMajor) {
- if ($thisPSVersionMinor -gt $splitRequiredPSVersionMinor) {
- $psVersionCheckResult = 'passed'
- $psVersionCheck = "(Major[$splitRequiredPSVersionMajor]; Minor[$thisPSVersionMinor] gt $($splitRequiredPSVersionMinor))"
- }
- else {
- if ($thisPSVersionPatch -ge $splitRequiredPSVersionPatch) {
- $psVersionCheckResult = 'passed'
- $psVersionCheck = "(Major[$splitRequiredPSVersionMajor]; Minor[$splitRequiredPSVersionMinor]; Patch[$thisPSVersionPatch] gt $($splitRequiredPSVersionPatch))"
- }
- else {
- $psVersionCheckResult = 'failed'
- $psVersionCheck = "(Major[$splitRequiredPSVersionMajor]; Minor[$splitRequiredPSVersionMinor]; Patch[$thisPSVersionPatch] lt $($splitRequiredPSVersionPatch))"
- }
- }
- }
- else {
- $psVersionCheckResult = 'failed'
- $psVersionCheck = "(Major[$splitRequiredPSVersionMajor] ne $($splitRequiredPSVersionMajor))"
- }
-
- if ($psVersionCheckResult -eq 'passed') {
- Write-Host " PS check $psVersionCheckResult : $($psVersionCheck); (minimum supported version '$requiredPSVersion')"
- Write-Host " PS Edition: $($PSVersionTable.PSEdition); PS Version: $($PSVersionTable.PSVersion)"
- Write-Host ' PS Version check succeeded' -ForegroundColor Green
- }
- else {
- Write-Host " PS check $psVersionCheckResult : $($psVersionCheck)"
- Write-Host " PS Edition: $($PSVersionTable.PSEdition); PS Version: $($PSVersionTable.PSVersion)"
- Write-Host " Parallelization requires Powershell 'Core' version '$($requiredPSVersion)' or higher"
- Throw 'Error - check the last console output for details'
- }
-}
-function validateAccess {
- #region validationAccess
- #validation / check 'Microsoft Graph API' Access
- $permissionCheckResults = @()
- if ($azAPICallConf['htParameters'].onAzureDevOpsOrGitHubActions -eq $true -or $azAPICallConf['htParameters'].accountType -eq 'ServicePrincipal' -or $azAPICallConf['htParameters'].accountType -eq 'ManagedService' -or $azAPICallConf['htParameters'].accountType -eq 'ClientAssertion') {
-
- Write-Host "Checking $($azAPICallConf['htParameters'].accountType) permissions"
-
- $permissionsCheckFailed = $false
-
- $currentTask = 'Test MSGraph Users Read permission'
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/users?`$count=true&`$top=1"
- $method = 'GET'
- $res = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -consistencyLevel 'eventual' -validateAccess
- if ($res -eq 'failed') {
- $permissionCheckResults += "MSGraph API 'Users Read' permission - check FAILED"
- $permissionsCheckFailed = $true
- }
- else {
- $permissionCheckResults += "MSGraph API 'Users Read' permission - check PASSED"
- }
-
- $currentTask = 'Test MSGraph Groups Read permission'
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/groups?`$count=true&`$top=1"
- $method = 'GET'
- $res = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -consistencyLevel 'eventual' -validateAccess
- if ($res -eq 'failed') {
- $permissionCheckResults += "MSGraph API 'Groups Read' permission - check FAILED"
- $permissionsCheckFailed = $true
- }
- else {
- $permissionCheckResults += "MSGraph API 'Groups Read' permission - check PASSED"
- }
-
- $currentTask = 'Test MSGraph ServicePrincipals Read permission'
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/servicePrincipals?`$count=true&`$top=1"
- $method = 'GET'
- $res = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -consistencyLevel 'eventual' -validateAccess
- if ($res -eq 'failed') {
- $permissionCheckResults += "MSGraph API 'ServicePrincipals Read' permission - check FAILED"
- $permissionsCheckFailed = $true
- }
- else {
- $permissionCheckResults += "MSGraph API 'ServicePrincipals Read' permission - check PASSED"
- }
-
- if (-not $NoPIMEligibility) {
- $currentTask = 'Test MSGraph PrivilegedAccess.Read.AzureResources permission'
- $uriExt = "&`$expand=parent&`$filter=(type eq 'subscription' or type eq 'managementgroup')&`$top=1"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/privilegedAccess/azureResources/resources?`$select=id,displayName,type,externalId" + $uriExt
- $res = AzAPICall -AzAPICallConfiguration $azapicallConf -uri $uri -currentTask $currentTask -validateAccess
- if ($res -eq 'failed') {
- $permissionCheckResults += "MSGraph API 'PrivilegedAccess.Read.AzureResources' permission - check FAILED - if you cannot grant this permission or you do not have an AAD Premium 2 license then use parameter -NoPIMEligibility"
- $permissionsCheckFailed = $true
- }
- else {
- $permissionCheckResults += "MSGraph API 'PrivilegedAccess.Read.AzureResources' permission - check PASSED"
- }
- }
- }
- #endregion validationAccess
-
- #ManagementGroup helper
- #region managementGroupHelper
- if (-not $ManagementGroupId) {
- #$catchResult = "letscheck"
- $currentTask = 'Getting all Management Groups'
- #Write-Host $currentTask
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups?api-version=2020-05-01"
- $method = 'GET'
- $getAzManagementGroups = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -validateAccess
-
- if ($getAzManagementGroups -eq 'failed') {
- $permissionCheckResults += "RBAC 'Reader' permissions on Management Group - check FAILED (use Id, not displayName)"
- $permissionsCheckFailed = $true
- }
- else {
- $permissionCheckResults += "RBAC 'Reader' permissions on Management Group - check PASSED"
- }
-
- Write-Host 'Permission check results'
- foreach ($permissionCheckResult in $permissionCheckResults) {
- if ($permissionCheckResult -like '*PASSED*') {
- Write-Host $permissionCheckResult -ForegroundColor Green
- }
- else {
- Write-Host $permissionCheckResult -ForegroundColor DarkRed
- }
- }
- if ($permissionsCheckFailed -eq $true) {
- Write-Host "Please consult the documentation: https://$($GithubRepository)#required-permissions-in-azure"
- Throw 'Error - Azure Governance Visualizer: check the last console output for details'
- }
-
- if ($getAzManagementGroups.Count -eq 0) {
- Write-Host 'Management Groups count returned null'
- Throw 'Error - Azure Governance Visualizer: check the last console output for details'
- }
- else {
- Write-Host "Detected $($getAzManagementGroups.Count) Management Groups"
- }
-
- [array]$MgtGroupArray = addIndexNumberToArray -array ($getAzManagementGroups)
- if (-not $MgtGroupArray) {
- Write-Host 'Seems you do not have access to any Management Group. Please make sure you have the required RBAC role [Reader] assigned on at least one Management Group' -ForegroundColor Red
- Throw 'Error - Azure Governance Visualizer: check the last console output for details'
- }
-
- selectMg
-
- if ($($MgtGroupArray[$SelectedMG - 1].Name)) {
- $script:ManagementGroupId = $($MgtGroupArray[$SelectedMG - 1].name)
- $script:ManagementGroupName = $($MgtGroupArray[$SelectedMG - 1].properties.displayName)
- }
- else {
- Write-Host 's.th. unexpected happened' -ForegroundColor Red
- return
- }
- Write-Host "Selected Management Group: #$($SelectedMG) $ManagementGroupName (Id: $ManagementGroupId)" -ForegroundColor Green
- Write-Host '_______________________________________'
- }
- else {
- $currentTask = "Checking permissions for ManagementGroup '$ManagementGroupId'"
- Write-Host $currentTask
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)?api-version=2020-05-01"
- $method = 'GET'
- $selectedManagementGroupId = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -listenOn 'Content' -validateAccess
-
- if ($selectedManagementGroupId -eq 'failed') {
- $permissionCheckResults += "RBAC 'Reader' permissions on Management Group '$($ManagementGroupId)' - check FAILED (use Id, not displayName)"
- $permissionsCheckFailed = $true
- }
- else {
- $permissionCheckResults += "RBAC 'Reader' permissions on Management Group '$($ManagementGroupId)' - check PASSED"
- $script:ManagementGroupId = $selectedManagementGroupId.Name
- $script:ManagementGroupName = $selectedManagementGroupId.properties.displayName
- }
-
- Write-Host 'Permission check results'
- foreach ($permissionCheckResult in $permissionCheckResults) {
- if ($permissionCheckResult -like '*PASSED*') {
- Write-Host $permissionCheckResult -ForegroundColor Green
- }
- else {
- Write-Host $permissionCheckResult -ForegroundColor DarkRed
- }
- }
-
- if ($permissionsCheckFailed -eq $true) {
- Write-Host "Please consult the documentation for permission requirements: https://$($GithubRepository)#technical-documentation"
- Throw 'Error - Azure Governance Visualizer: check the last console output for details'
- }
- }
- #endregion managementGroupHelper
-
- if ($azAPICallConf['htParameters'].accountType -eq 'User') {
- validateLeastPrivilegeForUser
- }
-}
-function validateLeastPrivilegeForUser {
- $currentTask = "Validate least priviledge (Azure Resource side) for executing user $($azapicallConf['htParameters'].userObjectId)"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01&`$filter=principalId eq '$($azapicallConf['htParameters'].userObjectId)'"
- $method = 'GET'
- $getRoleAssignmentsForExecutingUserAtManagementGroupId = AzAPICall -AzAPICallConfiguration $azapicallConf -uri $uri
- $nonReaderRolesAssigned = ($getRoleAssignmentsForExecutingUserAtManagementGroupId.properties.RoleDefinitionId | Sort-Object -Unique).where({ $_ -notlike '*acdd72a7-3385-48ef-bd42-f606fba81ae7' })
- if ($nonReaderRolesAssigned.Count -gt 0) {
- Write-Host '* * * LEAST PRIVILEGE ADVICE' -ForegroundColor DarkRed
- Write-Host 'The Azure Governance Visualizer script is executed with more permissions than required.'
- Write-Host "The executing identity '$($azapicallConf['checkContext'].Account.Id)' ($($azapicallConf['checkContext'].Account.Type)) Id: '$($azapicallConf['htparameters'].userObjectId)' has the following RBAC Role(s) assigned at Management Group scope '$ManagementGroupId':"
- foreach ($nonReaderRoleAssigned in $nonReaderRolesAssigned) {
- $currentTask = "Get RBAC Role definition '$nonReaderRoleAssigned'"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)$($nonReaderRoleAssigned)?api-version=2022-04-01"
- $method = 'GET'
- $getRole = AzAPICall -AzAPICallConfiguration $azapicallConf -uri $uri -listenOn Content
-
- if ($getRole.properties.roleName -eq 'owner' -or $getRole.properties.roleName -eq 'contributor') {
- Write-Host " - $($getRole.properties.roleName) ($($getRole.properties.type)) !!!"
- }
- else {
- Write-Host " - $($getRole.properties.roleName) ($($getRole.properties.type))"
- }
- }
- Write-Host "The required Azure RBAC role at Management Group scope '$ManagementGroupId' is 'Reader' (acdd72a7-3385-48ef-bd42-f606fba81ae7)."
- Write-Host "Recommendation: consider executing the script in context of a Service Principal with least privilege. Review the Azure Governance Visualizer Setup Guide at 'https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/blob/master/setup.md'"
- Write-Host ' * * * * * * * * * * * * * * * * * * * * * *' -ForegroundColor DarkRed
- Pause
- }
- else {
- Write-Host "Azure Governance Visualizer Least Privilege check (Azure Resource side) for executing identity '$($azapicallConf['checkContext'].Account.Id)' ($($azapicallConf['checkContext'].Account.Type)) Id: '$($azapicallConf['htparameters'].userObjectId)' succeeded" -ForegroundColor Green
- }
-}
-function verifyModules3rd {
- [CmdletBinding()]Param(
- [object]$modules
- )
-
- foreach ($module in $modules) {
- $moduleVersion = $module.ModuleVersion
-
- if ($moduleVersion) {
- Write-Host "Verify '$($module.ModuleName)' version '$moduleVersion'"
- }
- else {
- Write-Host "Verify '$($module.ModuleName)' (latest)"
- }
-
- $maxRetry = 3
- $tryCount = 0
- do {
- $tryCount++
- if ($tryCount -gt $maxRetry) {
- Write-Host " Managing '$($module.ModuleName)' failed (tried $($tryCount - 1)x)"
- throw " Managing '$($module.ModuleName)' failed"
- }
-
- $installModuleSuccess = $false
- try {
- if (-not $moduleVersion) {
- Write-Host ' Check latest module version'
- try {
- $moduleVersion = (Find-Module -Name $($module.ModuleName)).Version
- Write-Host " $($module.ModuleName) Latest module version: $moduleVersion"
- }
- catch {
- Write-Host " $($module.ModuleName) - Check latest module version failed"
- throw " $($module.ModuleName) - Check latest module version failed"
- }
- }
-
- if (-not $installModuleSuccess) {
- try {
- $moduleVersionLoaded = (Get-InstalledModule -Name $($module.ModuleName)).Version
- if ([System.Version]$moduleVersionLoaded -eq [System.Version]$moduleVersion) {
- $installModuleSuccess = $true
- }
- else {
- Write-Host " $($module.ModuleName) - Deviating module version '$moduleVersionLoaded'"
- if ([System.Version]$moduleVersionLoaded -gt [System.Version]$moduleVersion) {
- if (($env:SYSTEM_TEAMPROJECTID -and $env:BUILD_REPOSITORY_ID) -or $env:GITHUB_ACTIONS) {
- #AzDO or GH
- throw " $($module.ModuleName) - Deviating module version $moduleVersionLoaded"
- }
- else {
- Write-Host " Current module version '$moduleVersionLoaded' greater than the minimum required version '$moduleVersion' -> tolerated" -ForegroundColor Yellow
- $installModuleSuccess = $true
- }
- }
- else {
- Write-Host " Current module version '$moduleVersionLoaded' lower than the minimum required version '$moduleVersion' -> failed"
- throw " $($module.ModuleName) - Deviating module version $moduleVersionLoaded"
- }
- }
- }
- catch {
- throw
- }
- }
- }
- catch {
- Write-Host " '$($module.ModuleName) $moduleVersion' not installed"
- if (($env:SYSTEM_TEAMPROJECTID -and $env:BUILD_REPOSITORY_ID) -or $env:GITHUB_ACTIONS) {
- Write-Host " Installing $($module.ModuleName) module ($($moduleVersion))"
- $installAzAPICallModuleTryCounter = 0
- do {
- $installAzAPICallModuleTryCounter++
- try {
- $params = @{
- Name = "$($module.ModuleName)"
- Force = $true
- RequiredVersion = $moduleVersion
- ErrorAction = 'Stop'
- }
- Install-Module @params
- $installAzAPICallModuleSuccess = $true
- Write-Host " Try#$($installAzAPICallModuleTryCounter) Installing '$($module.ModuleName)' module ($($moduleVersion)) succeeded"
- }
- catch {
- Write-Host " Try#$($installAzAPICallModuleTryCounter) Installing '$($module.ModuleName)' module ($($moduleVersion)) failed - sleep $($installAzAPICallModuleTryCounter) seconds"
- Start-Sleep -Seconds $installAzAPICallModuleTryCounter
- $installAzAPICallModuleSuccess = $false
- }
- }
- until($installAzAPICallModuleTryCounter -gt 10 -or $installAzAPICallModuleSuccess)
- if (-not $installAzAPICallModuleSuccess) {
- throw " Installing '$($module.ModuleName)' module ($($moduleVersion)) failed"
- }
-
- }
- else {
- do {
- $installModuleUserChoice = $null
- $installModuleUserChoice = Read-Host " Do you want to install $($module.ModuleName) module ($($moduleVersion)) from the PowerShell Gallery? (y/n)"
- if ($installModuleUserChoice -eq 'y') {
- try {
- Install-Module -Name $module.ModuleName -RequiredVersion $moduleVersion -Force -ErrorAction Stop
- try {
- Import-Module -Name $module.ModuleName -RequiredVersion $moduleVersion -Force -ErrorAction Stop
- }
- catch {
- throw " 'Import-Module -Name $($module.ModuleName) -RequiredVersion $moduleVersion -Force' failed"
- }
- }
- catch {
- throw " 'Install-Module -Name $($module.ModuleName) -RequiredVersion $moduleVersion' failed"
- }
- }
- elseif ($installModuleUserChoice -eq 'n') {
- Write-Host " $($module.ModuleName) module is required, please visit https://aka.ms/$($module.ModuleProductName) or https://www.powershellgallery.com/packages/$($module.ModuleProductName)"
- throw " $($module.ModuleName) module is required"
- }
- else {
- Write-Host " Accepted input 'y' or 'n'; start over.."
- }
- }
- until ($installModuleUserChoice -eq 'y')
- }
- }
- }
- until ($installModuleSuccess)
- Write-Host " Verify '$($module.ModuleName)' version '$moduleVersion' succeeded" -ForegroundColor Green
- }
-}
-#region functions4DataCollection
-
-function dataCollectionMGSecureScore {
- [CmdletBinding()]Param(
- [string]$Id
- )
-
- $mgAscSecureScoreResult = ''
- if ($azAPICallConf['htParameters'].NoMDfCSecureScore -eq $false) {
- if ($htMgASCSecureScore.($Id)) {
- $mgAscSecureScoreResult = $htMgASCSecureScore.($Id).SecureScore
- }
- else {
- $mgAscSecureScoreResult = 'isNullOrEmpty'
- }
- }
- return $mgAscSecureScoreResult
-}
-$funcDataCollectionMGSecureScore = $function:dataCollectionMGSecureScore.ToString()
-
-function dataCollectionDefenderPlans {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $ChildMgMgPath,
- $SubscriptionQuotaId
- )
-
- $currentTask = "Getting Microsoft Defender for Cloud plans for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Security/pricings?api-version=2018-06-01"
- $method = 'GET'
- $defenderPlansResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- if ($defenderPlansResult -eq 'SubScriptionNotRegistered' -or $defenderPlansResult -eq 'DisallowedProvider') {
- #Subscription skipped for MDfC
- $null = $script:arrayDefenderPlansSubscriptionsSkipped.Add([PSCustomObject]@{
- subscriptionId = $scopeId
- subscriptionName = $scopeDisplayName
- subscriptionQuotaId = $subscriptionQuotaId
- subscriptionMgPath = $childMgMgPath
- reason = $defenderPlansResult
- })
- }
- else {
- if ($defenderPlansResult.Count -gt 0) {
- foreach ($defenderPlanResult in $defenderPlansResult) {
- $null = $script:arrayDefenderPlans.Add([PSCustomObject]@{
- subscriptionId = $scopeId
- subscriptionName = $scopeDisplayName
- subscriptionMgPath = $childMgMgPath
- defenderPlan = $defenderPlanResult.name
- defenderPlanTier = $defenderPlanResult.properties.pricingTier
- })
- }
- }
- }
-}
-$funcDataCollectionDefenderPlans = $function:dataCollectionDefenderPlans.ToString()
-
-
-function dataCollectionAdvisorScores {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $ChildMgMgPath,
- $SubscriptionQuotaId
- )
-
- $currentTask = "Getting Advisor Scores for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Advisor/advisorScore?api-version=2020-07-01-preview"
- $method = 'GET'
- $advisorScoreResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection' -skipOnErrorCode 404
-
- if ($advisorScoreResult -eq 'SubScriptionNotRegistered' -or $advisorScoreResult -eq 'DisallowedProvider') {
- }
- else {
- if ($advisorScoreResult -like 'azgvzerrorMessage_*') {
-
- }
- else {
- if ($advisorScoreResult.Count -gt 0) {
- foreach ($entry in $advisorScoreResult) {
- #Write-Host ($entry | ConvertTo-Json -Depth 99)
- if ($entry.Name) {
- $objectGuid = [System.Guid]::empty
- if ([System.Guid]::TryParse($entry.Name, [System.Management.Automation.PSReference]$ObjectGuid)) {
- }
- else {
- $null = $script:arrayAdvisorScores.Add([PSCustomObject]@{
- subscriptionId = $scopeId
- subscriptionName = $scopeDisplayName
- subscriptionQuotaId = $SubscriptionQuotaId
- subscriptionMgPath = $childMgMgPath
- category = $entry.Name
- score = $entry.properties.lastRefreshedScore.score
- })
- }
- }
- }
- }
- }
- }
-}
-$funcDataCollectionAdvisorScores = $function:dataCollectionAdvisorScores.ToString()
-
-function dataCollectionDefenderEmailContacts {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $SubscriptionQuotaId
- )
-
- $currentTask = "Getting Microsoft Defender for Cloud Email contacts for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Security/securityContacts?api-version=2020-01-01-preview"
- $method = 'GET'
- $defenderSecurityContactsResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -listenOn 'Content' -currentTask $currentTask -caller 'CustomDataCollection'
-
- if ($defenderSecurityContactsResult -eq 'SubScriptionNotRegistered' -or $defenderSecurityContactsResult -eq 'DisallowedProvider') {
- }
- else {
-
- if ($defenderSecurityContactsResult -like 'azgvzerrorMessage_*') {
- $errorInfo = $defenderSecurityContactsResult -replace 'azgvzerrorMessage_'
- $script:htDefenderEmailContacts.($scopeId) = @{
- subscriptionId = $scopeId
- subscriptionName = $scopeDisplayName
- emails = $errorInfo
- roles = $errorInfo
- alertNotificationsState = $errorInfo
- alertNotificationsminimalSeverity = $errorInfo
- }
- }
- else {
- if ($defenderSecurityContactsResult.Count -gt 0) {
- foreach ($entry in $defenderSecurityContactsResult) {
-
- if ($entry.properties) {
- if ($entry.properties.notificationsByRole.roles.count -gt 0) {
- $roles = ($entry.properties.notificationsByRole.roles | Sort-Object) -join "$CsvDelimiterOpposite "
- }
- else {
- $roles = 'none'
- }
-
- if ($entry.properties.emails) {
- if (-not [string]::IsNullOrWhiteSpace($entry.properties.emails)) {
- $emailsSplitted = $entry.properties.emails -split ';'
- $arrayEmails = @()
- foreach ($email in $emailsSplitted) {
- $arrayEmails += "'$email'"
- }
- $emails = ($arrayEmails | Sort-Object) -join "$CsvDelimiterOpposite "
- }
- else {
- $emails = $entry.properties.emails
- }
- }
- else {
- $emails = 'none'
- }
-
- if ($entry.properties.alertNotifications.state) {
- $alertNotificationsState = $entry.properties.alertNotifications.state
- }
-
- if ($entry.properties.alertNotifications.minimalSeverity) {
- $alertNotificationsminimalSeverity = $entry.properties.alertNotifications.minimalSeverity
- }
- }
- else {
- $roles = 'n/a'
- $emails = 'n/a'
- $alertNotificationsState = 'n/a'
- $alertNotificationsminimalSeverity = 'n/a'
- }
-
- $script:htDefenderEmailContacts.($scopeId) = @{
- subscriptionId = $scopeId
- subscriptionName = $scopeDisplayName
- emails = $emails
- roles = $roles
- alertNotificationsState = $alertNotificationsState
- alertNotificationsminimalSeverity = $alertNotificationsminimalSeverity
- }
- }
- }
- else {
- $script:htDefenderEmailContacts.($scopeId) = @{
- subscriptionId = $scopeId
- subscriptionName = $scopeDisplayName
- emails = 'n/a'
- roles = 'n/a'
- alertNotificationsState = 'n/a'
- alertNotificationsminimalSeverity = 'n/a'
- }
- }
- }
- }
-}
-$funcDataCollectionDefenderEmailContacts = $function:dataCollectionDefenderEmailContacts.ToString()
-
-function dataCollectionVNets {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $SubscriptionQuotaId
- )
-
- $currentTask = "Getting Virtual Networks for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Network/virtualNetworks?api-version=2022-05-01"
- $method = 'GET'
- $networkResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- if ($networkResult -eq 'someError') {
- }
- else {
- if ($networkResult.Count -gt 0) {
- if ($networkResult -ne 'DisallowedProvider') {
- foreach ($vnet in $networkResult) {
- $null = $script:arrayVNets.Add($vnet)
- }
- }
- }
- }
-}
-$funcDataCollectionVNets = $function:dataCollectionVNets.ToString()
-
-function dataCollectionPrivateEndpoints {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $SubscriptionQuotaId
- )
-
- $currentTask = "Getting Private Endpoints for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Network/privateEndpoints?api-version=2022-05-01"
- $method = 'GET'
- $privateEndpointsResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection' -unhandledErrorAction Continue
-
- if ($privateEndpointsResult.Count -gt 0) {
- if ($privateEndpointsResult -ne 'DisallowedProvider') {
- foreach ($pe in $privateEndpointsResult) {
- $null = $script:arrayPrivateEndPoints.Add($pe)
- }
- }
- }
-}
-$funcDataCollectionPrivateEndpoints = $function:dataCollectionPrivateEndpoints.ToString()
-
-function dataCollectionDiagnosticsSub {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $ChildMgMgPath,
- $ChildMgId,
- $subscriptionQuotaId
- )
-
- $currentTask = "Getting Diagnostic Settings for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/microsoft.insights/diagnosticSettings?api-version=2021-05-01-preview"
- $method = 'GET'
- $getDiagnosticSettingsSub = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- if ($getDiagnosticSettingsSub.Count -eq 0) {
- $null = $script:arrayDiagnosticSettingsMgSub.Add([PSCustomObject]@{
- Scope = 'Sub'
- ScopeName = $scopeDisplayName
- ScopeId = $scopeId
- ScopeMgPath = $childMgMgPath
- SubMgParent = $childMgId
- DiagnosticsPresent = 'false'
- })
- }
- else {
- foreach ($diagnosticSetting in $getDiagnosticSettingsSub) {
- $arrayLogs = [System.Collections.ArrayList]@()
- if ($diagnosticSetting.Properties.logs) {
- foreach ($logCategory in $diagnosticSetting.properties.logs) {
- $null = $arrayLogs.Add([PSCustomObject]@{
- Category = $logCategory.category
- Enabled = $logCategory.enabled
- })
- }
- }
-
- $htLogs = @{}
- if ($diagnosticSetting.Properties.logs) {
- foreach ($logCategory in $diagnosticSetting.properties.logs) {
- if ($logCategory.enabled) {
- $htLogs.($logCategory.category) = 'true'
- }
- else {
- $htLogs.($logCategory.category) = 'false'
- }
- }
- }
-
- if ($diagnosticSetting.Properties.workspaceId) {
- $null = $script:arrayDiagnosticSettingsMgSub.Add([PSCustomObject]@{
- Scope = 'Sub'
- ScopeName = $scopeDisplayName
- ScopeId = $scopeId
- ScopeMgPath = $childMgMgPath
- SubMgParent = $childMgId
- DiagnosticsPresent = 'true'
- DiagnosticSettingName = $diagnosticSetting.name
- DiagnosticTargetType = 'LA'
- DiagnosticTargetId = $diagnosticSetting.Properties.workspaceId
- DiagnosticCategories = $arrayLogs
- DiagnosticCategoriesHt = $htLogs
- })
- }
- if ($diagnosticSetting.Properties.storageAccountId) {
- $null = $script:arrayDiagnosticSettingsMgSub.Add([PSCustomObject]@{
- Scope = 'Sub'
- ScopeName = $scopeDisplayName
- ScopeId = $scopeId
- ScopeMgPath = $childMgMgPath
- SubMgParent = $childMgId
- DiagnosticsPresent = 'true'
- DiagnosticSettingName = $diagnosticSetting.name
- DiagnosticTargetType = 'SA'
- DiagnosticTargetId = $diagnosticSetting.Properties.storageAccountId
- DiagnosticCategories = $arrayLogs
- DiagnosticCategoriesHt = $htLogs
- })
- }
- if ($diagnosticSetting.Properties.eventHubAuthorizationRuleId) {
- $null = $script:arrayDiagnosticSettingsMgSub.Add([PSCustomObject]@{
- Scope = 'Sub'
- ScopeName = $scopeDisplayName
- ScopeId = $scopeId
- ScopeMgPath = $childMgMgPath
- SubMgParent = $childMgId
- DiagnosticsPresent = 'true'
- DiagnosticSettingName = $diagnosticSetting.name
- DiagnosticTargetType = 'EH'
- DiagnosticTargetId = $diagnosticSetting.Properties.eventHubAuthorizationRuleId
- DiagnosticCategories = $arrayLogs
- DiagnosticCategoriesHt = $htLogs
- })
- }
- }
- }
-}
-$funcDataCollectionDiagnosticsSub = $function:dataCollectionDiagnosticsSub.ToString()
-
-function dataCollectionDiagnosticsMG {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName
- )
-
- $mgPath = $htManagementGroupsMgPath.($scopeId).pathDelimited
- $currentTask = "Getting Diagnostic Settings for Management Group: '$($scopeDisplayName)' ('$($scopeId)')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($mgdetail.Name)/providers/microsoft.insights/diagnosticSettings?api-version=2020-01-01-preview"
- $method = 'GET'
- $getDiagnosticSettingsMg = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
-
- if ($getDiagnosticSettingsMg -eq 'InvalidResourceType') {
- #skipping until supported
- }
- else {
- if ($getDiagnosticSettingsMg.Count -eq 0) {
- $null = $script:arrayDiagnosticSettingsMgSub.Add([PSCustomObject]@{
- Scope = 'Mg'
- ScopeName = $scopeDisplayName
- ScopeId = $scopeId
- ScopeMgPath = $mgPath
- DiagnosticsPresent = 'false'
- DiagnosticsInheritedOrnot = $false
- DiagnosticsInheritedFrom = 'none'
- })
- }
- else {
- foreach ($diagnosticSetting in $getDiagnosticSettingsMg) {
- $arrayLogs = [System.Collections.ArrayList]@()
- if ($diagnosticSetting.Properties.logs) {
- foreach ($logCategory in $diagnosticSetting.properties.logs) {
- $null = $arrayLogs.Add([PSCustomObject]@{
- Category = $logCategory.category
- Enabled = $logCategory.enabled
- })
- }
- }
-
- $htLogs = @{}
- if ($diagnosticSetting.Properties.logs) {
- foreach ($logCategory in $diagnosticSetting.properties.logs) {
- if ($logCategory.enabled) {
- $htLogs.($logCategory.category) = 'true'
- }
- else {
- $htLogs.($logCategory.category) = 'false'
- }
- }
- }
-
- if ($diagnosticSetting.Properties.workspaceId) {
- $null = $script:arrayDiagnosticSettingsMgSub.Add([PSCustomObject]@{
- Scope = 'Mg'
- ScopeName = $scopeDisplayName
- ScopeId = $scopeId
- ScopeMgPath = $mgPath
- DiagnosticsPresent = 'true'
- DiagnosticsInheritedOrnot = $false
- DiagnosticsInheritedFrom = 'none'
- DiagnosticSettingName = $diagnosticSetting.name
- DiagnosticTargetType = 'LA'
- DiagnosticTargetId = $diagnosticSetting.Properties.workspaceId
- DiagnosticCategories = $arrayLogs
- DiagnosticCategoriesHt = $htLogs
- })
- }
- if ($diagnosticSetting.Properties.storageAccountId) {
- $null = $script:arrayDiagnosticSettingsMgSub.Add([PSCustomObject]@{
- Scope = 'Mg'
- ScopeName = $scopeDisplayName
- ScopeId = $scopeId
- ScopeMgPath = $mgPath
- DiagnosticsPresent = 'true'
- DiagnosticsInheritedOrnot = $false
- DiagnosticsInheritedFrom = 'none'
- DiagnosticSettingName = $diagnosticSetting.name
- DiagnosticTargetType = 'SA'
- DiagnosticTargetId = $diagnosticSetting.Properties.storageAccountId
- DiagnosticCategories = $arrayLogs
- DiagnosticCategoriesHt = $htLogs
- })
- }
- if ($diagnosticSetting.Properties.eventHubAuthorizationRuleId) {
- $null = $script:arrayDiagnosticSettingsMgSub.Add([PSCustomObject]@{
- Scope = 'Mg'
- ScopeName = $scopeDisplayName
- ScopeId = $scopeId
- ScopeMgPath = $mgPath
- DiagnosticsPresent = 'true'
- DiagnosticsInheritedOrnot = $false
- DiagnosticsInheritedFrom = 'none'
- DiagnosticSettingName = $diagnosticSetting.name
- DiagnosticTargetType = 'EH'
- DiagnosticTargetId = $diagnosticSetting.Properties.eventHubAuthorizationRuleId
- DiagnosticCategories = $arrayLogs
- DiagnosticCategoriesHt = $htLogs
- })
- }
- }
- }
- }
-}
-$funcDataCollectionDiagnosticsMG = $function:dataCollectionDiagnosticsMG.ToString()
-
-function dataCollectionStorageAccounts {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $ChildMgMgPath,
- $ChildMgParentNameChainDelimited,
- $subscriptionQuotaId
- )
-
- $currentTask = "Getting Storage Accounts for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Storage/storageAccounts?api-version=2021-09-01"
- $method = 'GET'
- $storageAccountsSubscriptionResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- if ($storageAccountsSubscriptionResult -ne 'DisallowedProvider') {
- foreach ($storageAccount in $storageAccountsSubscriptionResult) {
-
- $dtisostart = Get-Date (Get-Date).AddHours(-1).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z'
- $dtisoend = Get-Date (Get-Date).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z'
- $currentTask = "Getting Storage Account '$($storageAccount.name)' UsedCapacity ('$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId'])"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)$($storageAccount.id)/providers/Microsoft.Insights/metrics?timespan=$($dtisostart)/$($dtisoend)&metricnames=UsedCapacity&aggregation=Average&api-version=2021-05-01"
- $method = 'GET'
- $storageAccountUsedCapacity = $null
- $storageAccountUsedCapacity = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection' -unhandledErrorAction Continue
-
- $usedCapacity = 'n/a'
- if ($storageAccountUsedCapacity.Count -gt 0) {
- if (-not [string]::IsNullOrWhiteSpace($storageAccountUsedCapacity.timeseries.data.average)) {
- $usedCapacity = [decimal]$storageAccountUsedCapacity.timeseries.data.average / 1024 / 1024 / 1024
- }
- }
-
- $obj = [System.Collections.ArrayList]@()
- $null = $obj.Add([PSCustomObject]@{
- SA = $storageAccount
- SAUsedCapacity = $usedCapacity
- })
- $null = $script:storageAccounts.Add($obj)
- }
- }
-
-}
-$funcDataCollectionStorageAccounts = $function:dataCollectionStorageAccounts.ToString()
-
-function dataCollectionResources {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $ChildMgMgPath,
- $ChildMgParentNameChainDelimited,
- $subscriptionQuotaId
- )
-
- #region resources LIST
- $currentTask = "Getting Resources for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/resources?`$expand=createdTime,changedTime,properties&api-version=2023-07-01"
- $method = 'GET'
- $resourcesSubscriptionResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
- #Write-Host 'arm resList count:'$resourcesSubscriptionResult.Count
- #endregion resources LIST
-
- #region resources GET
- if ($resourcesSubscriptionResult.Count -gt 0) {
- $arrayResourcesWithProperties = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
- $resourcesSubscriptionResult | ForEach-Object -Parallel {
- $resource = $_
-
- #region using
- $arrayResourcesWithProperties = $using:arrayResourcesWithProperties
- $htResourceProvidersRef = $using:htResourceProvidersRef
- $arrayPrivateEndPointsFromResourceProperties = $using:arrayPrivateEndPointsFromResourceProperties
- $htAvailablePrivateEndpointTypes = $using:htAvailablePrivateEndpointTypes
- $htResourcePropertiesConvertfromJSONFailed = $using:htResourcePropertiesConvertfromJSONFailed
- $scopeId = $using:scopeId
- $scopeDisplayName = $using:scopeDisplayName
- $ChildMgParentNameChainDelimited = $using:ChildMgParentNameChainDelimited
- $azAPICallConf = $using:azAPICallConf
- #$htResourcesWithProperties = $using:htResourcesWithProperties
- #endregion using
-
- if ($htAvailablePrivateEndpointTypes.(($resource.type).ToLower())) {
- #Write-Host "$($resource.type) in `$htAvailablePrivateEndpointTypes"
- if ($htResourceProvidersRef.($resource.type)) {
- if ($htResourceProvidersRef.($resource.type).APIDefault) {
- $apiVersionToUse = $htResourceProvidersRef.($resource.type).APIDefault
- $apiRef = 'default'
- }
- else {
- $apiVersionToUse = $htResourceProvidersRef.($resource.type).APILatest
- $apiRef = 'latest'
- }
-
- $currentTask = "Getting Resource Properties API-version: '$apiVersionToUse' ($apiRef); ResourceType: '$($resource.type)'; ResourceId: '$($resource.id)'"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)$($resource.id)?api-version=$apiVersionToUse"
- $method = 'GET'
- $resourceResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection' -listenOn Content -unhandledErrorAction Continue
-
- if ($resourceResult -ne 'ResourceOrResourcegroupNotFound' -and $resourceResult -ne 'convertfromJSONError') {
- $null = $script:arrayResourcesWithProperties.Add($resourceResult)
- #$script:htResourcesWithProperties.($resourceResult.id) = $resourceResult
- if ($resourceResult.properties.privateEndpointConnections.Count -gt 0) {
- foreach ($privateEndpointConnection in $resourceResult.properties.privateEndpointConnections) {
- $resourceResultIdSplit = $resourceResult.id -split '/'
- $null = $script:arrayPrivateEndPointsFromResourceProperties.Add([PSCustomObject]@{
- ResourceName = $resourceResult.name
- ResourceType = $resourceResult.type
- ResourceId = $resourceResult.id
- ResourceResourceGroup = $resourceResultIdSplit[4]
- ResourceSubscriptionId = $scopeId
- ResourceSubscriptionName = $scopeDisplayName
- ResourceMGPath = $ChildMgParentNameChainDelimited
- privateEndpointConnection = $privateEndpointConnection
- })
- }
- }
- }
- else {
- if ($resourceResult -eq 'convertfromJSONError') {
- $script:htResourcePropertiesConvertfromJSONFailed.($resource.id) = @{}
- }
- }
- }
- else {
- Write-Host "[Azure Governance Visualizer] Please file an issue at the Azure Governance Visualizer GitHub repository (aka.ms/AzGovViz) and provide this information (scrub subscription Id and company identifyable names): No API-version matches! ResourceType: '$($resource.type)'; ResourceId: '$($resource.id)' - Thank you!" -ForegroundColor DarkRed
- }
- }
- else {
- #Write-Host "$($resource.type) not in `$htAvailablePrivateEndpointTypes"
- }
-
- } -ThrottleLimit $azAPICallConf['htParameters'].ThrottleLimit
- }
- #Write-Host 'arm resGet count:' $arrayResourcesWithProperties.Count
- #endregion resources GET
-
- # if ($resourcesSubscriptionResult.Count -ne $arrayResourcesWithProperties.Count) {
- # Write-Host " FYI: Getting Resources for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId'] - ARM list count: $($resourcesSubscriptionResult.Count); ARG get count: $($arrayResourcesWithProperties.Count)"
- # }
-
- #region PSRule
- if ($azAPICallConf['htParameters'].DoPSRule -eq $true) {
- if ($resourcesSubscriptionResult.Count -gt 0) {
-
- $startPSRule = Get-Date
- try {
- <#
- $path = (Get-Module PSRule.Rules.Azure -ListAvailable | Sort-Object Version -Descending -Top 1).ModuleBase
- Write-Host "Import-Module (Join-Path $path -ChildPath 'PSRule.Rules.Azure-nodeps.psd1')"
- Import-Module (Join-Path $path -ChildPath 'PSRule.Rules.Azure-nodeps.psd1')
- #>
- if ($azAPICallConf['htParameters'].PSRuleFailedOnly -eq $true) {
- $psruleResults = $arrayResourcesWithProperties | Invoke-PSRule -Module psrule.rules.Azure -As Detail -Culture en-us -WarningAction Ignore -ErrorAction SilentlyContinue -Outcome Fail, Error
- }
- else {
- $psruleResults = $arrayResourcesWithProperties | Invoke-PSRule -Module psrule.rules.Azure -As Detail -Culture en-us -WarningAction Ignore -ErrorAction SilentlyContinue
- }
- }
- catch {
- Write-Host " Please report 'PSRule for Azure' error '$($scopeDisplayName)' ('$scopeId'): $_"
- }
-
- $endPSRule = Get-Date
- $durationPSRule = $((New-TimeSpan -Start $startPSRule -End $endPSRule).TotalSeconds)
-
- $null = $script:arrayPSRuleTracking.Add([PSCustomObject]@{
- subscriptionId = $scopeId
- duration = $durationPSRule
- })
-
- if ($psruleResults.Count -gt 0) {
- foreach ($psRuleResult in $psRuleResults) {
- $null = $script:arrayPSRule.Add([PSCustomObject]@{
- resourceType = $psRuleResult.TargetType
- subscriptionId = $scopeId
- mgPath = $ChildMgParentNameChainDelimited
- resourceId = $psRuleResult.TargetObject.id
- pillar = $psRuleResult.Info.Annotations.pillar
- category = $psRuleResult.Info.Annotations.category
- severity = $psRuleResult.Info.Annotations.severity
- rule = $psRuleResult.Info.DisplayName
- description = $psRuleResult.Info.Description
- recommendation = $psRuleResult.Info.Recommendation
- link = $psRuleResult.Info.Annotations.'online version'
- result = $psRuleResult.Outcome
- errorMsg = $psRuleResult.Error.Message
- })
- }
- }
- }
- }
- #endregion PSRule
-
- foreach ($resourceTypeLocation in ($resourcesSubscriptionResult | Group-Object -Property type, location)) {
- $null = $script:resourcesAll.Add([PSCustomObject]@{
- subscriptionId = $scopeId
- type = ($resourceTypeLocation.values[0]).ToLower()
- location = ($resourceTypeLocation.values[1]).ToLower()
- count_ = $resourceTypeLocation.Count
- })
- }
-
- foreach ($resourceType in ($resourcesSubscriptionResult | Group-Object -Property type)) {
- if (-not $htResourceTypesUniqueResource.(($resourceType.name).ToLower())) {
- $script:htResourceTypesUniqueResource.(($resourceType.name).ToLower()) = @{}
- $script:htResourceTypesUniqueResource.(($resourceType.name).ToLower()).resourceId = $resourceType.Group.Id | Select-Object -First 1
- }
- }
-
- $startSubResourceIdsThis = Get-Date
-
- <# Build the $JSONcafResourceNaming #pending PR https://github.com/MicrosoftDocs/cloud-adoption-framework/pull/916
- $arrayCAFNamingConvention = [System.Collections.ArrayList]@()
- $htCAFNamingConvention = @{}
- #$cafNamingFromFile = Get-Content -Path .\cafNaming.md -Encoding utf8
- $CAFFileName = 'resource-abbreviations.md'
- Invoke-webrequest -OutFile .\$($CAFFileName) -URI "https://raw.githubusercontent.com/MicrosoftDocs/cloud-adoption-framework/main/docs/ready/azure-best-practices/resource-abbreviations.md"
- $cafNamingFromFile = Get-Content -Path .\$($CAFFileName) -Encoding utf8
- $cafNamingFromFile.count
- foreach ($line in $cafNamingFromFile) {
- #$line
- if ($line -match "microsoft.") {
- $tranformed = $line -replace '`' -split " \| "
- $friendlyName = $($tranformed[0] -replace "\| ")
- $resourceType = $($tranformed[1])
- $namingConvention = $($tranformed[2] -replace " \|" -replace "\|")
- $null = $arrayCAFNamingConvention.Add([PSCustomObject]@{
- resourceType = $resourceType
- friendlyName = $friendlyName
- namingConvention = $namingConvention
- })
- }
- }
-
- $htCAFNamingConvention = [ordered]@{}
- $arrayCAFNamingConventionGroupedByType = $arrayCAFNamingConvention | Sort-Object -Property resourceType | Group-Object -Property resourceType
- foreach ($entry in $arrayCAFNamingConventionGroupedByType){
- $htCAFNamingConvention.($entry.name) = @{}
- $htCAFNamingConvention.($entry.name).friendlyName = $entry.group.friendlyName
- $htCAFNamingConvention.($entry.name).namingConvention = $entry.group.namingConvention
- }
- $htCAFNamingConvention | ConvertTo-Json
-#>
-
- $JSONcafResourceNaming = @'
- {
- "Microsoft.AnalysisServices/servers": {
- "friendlyName": "Azure Analysis Services server",
- "namingConvention": "as"
- },
- "Microsoft.ApiManagement/service": {
- "friendlyName": "API management service instance",
- "namingConvention": "apim-"
- },
- "Microsoft.AppConfiguration/configurationStores": {
- "friendlyName": "App Configuration store",
- "namingConvention": "appcs-"
- },
- "Microsoft.Authorization/policyDefinitions": {
- "friendlyName": "Policy definition",
- "namingConvention": "policy-"
- },
- "Microsoft.Automation/automationAccounts": {
- "friendlyName": "Automation account",
- "namingConvention": "aa-"
- },
- "Microsoft.Blueprint/blueprints": {
- "friendlyName": "Blueprint",
- "namingConvention": "bp-"
- },
- "Microsoft.Blueprint/blueprints/artifacts": {
- "friendlyName": "Blueprint assignment",
- "namingConvention": "bpa-"
- },
- "Microsoft.Cache/Redis": {
- "friendlyName": "Azure Cache for Redis instance",
- "namingConvention": "redis-"
- },
- "Microsoft.Cdn/profiles": {
- "friendlyName": "CDN profile",
- "namingConvention": "cdnp-"
- },
- "Microsoft.Cdn/profiles/endpoints": {
- "friendlyName": "CDN endpoint",
- "namingConvention": "cdne-"
- },
- "Microsoft.CognitiveServices/accounts": {
- "friendlyName": "Azure Cognitive Services",
- "namingConvention": "cog-"
- },
- "Microsoft.Compute/availabilitySets": {
- "friendlyName": "Availability set",
- "namingConvention": "avail-"
- },
- "Microsoft.Compute/cloudServices": {
- "friendlyName": "Cloud service",
- "namingConvention": "cld-"
- },
- "Microsoft.Compute/diskEncryptionSets": {
- "friendlyName": "Disk encryption set",
- "namingConvention": "des"
- },
- "Microsoft.Compute/disks": {
- "friendlyName": [
- "Managed disk (data)",
- "Managed disk (OS)"
- ],
- "namingConvention": [
- "disk",
- "osdisk"
- ]
- },
- "Microsoft.Compute/galleries": {
- "friendlyName": "Gallery",
- "namingConvention": "gal"
- },
- "Microsoft.Compute/snapshots": {
- "friendlyName": "Snapshot",
- "namingConvention": "snap-"
- },
- "Microsoft.Compute/virtualMachines": {
- "friendlyName": "Virtual machine",
- "namingConvention": "vm"
- },
- "Microsoft.Compute/virtualMachineScaleSets": {
- "friendlyName": "Virtual machine scale set",
- "namingConvention": "vmss-"
- },
- "Microsoft.ContainerInstance/containerGroups": {
- "friendlyName": "Container instance",
- "namingConvention": "ci"
- },
- "Microsoft.ContainerRegistry/registries": {
- "friendlyName": "Container registry",
- "namingConvention": "cr"
- },
- "Microsoft.ContainerService/managedClusters": {
- "friendlyName": "AKS cluster",
- "namingConvention": "aks-"
- },
- "Microsoft.Databricks/workspaces": {
- "friendlyName": "Azure Databricks workspace",
- "namingConvention": "dbw-"
- },
- "Microsoft.DataFactory/factories": {
- "friendlyName": "Azure Data Factory",
- "namingConvention": "adf-"
- },
- "Microsoft.DataLakeAnalytics/accounts": {
- "friendlyName": "Data Lake Analytics account",
- "namingConvention": "dla"
- },
- "Microsoft.DataLakeStore/accounts": {
- "friendlyName": "Data Lake Store account",
- "namingConvention": "dls"
- },
- "Microsoft.DataMigration/services": {
- "friendlyName": "Database Migration Service instance",
- "namingConvention": "dms-"
- },
- "Microsoft.DataProtection/BackupVaults": {
- "friendlyName": "Backup vault",
- "namingConvention": "bv-"
- },
- "Microsoft.DBforMySQL/servers": {
- "friendlyName": "MySQL database",
- "namingConvention": "mysql-"
- },
- "Microsoft.DBforPostgreSQL/servers": {
- "friendlyName": "PostgreSQL database",
- "namingConvention": "psql-"
- },
- "Microsoft.Devices/IotHubs": {
- "friendlyName": "IoT hub",
- "namingConvention": "iot-"
- },
- "Microsoft.Devices/provisioningServices": {
- "friendlyName": "Provisioning services",
- "namingConvention": "provs-"
- },
- "Microsoft.Devices/provisioningServices/certificates": {
- "friendlyName": "Provisioning services certificate",
- "namingConvention": "pcert-"
- },
- "Microsoft.DocumentDB/databaseAccounts/sqlDatabases": {
- "friendlyName": "Azure Cosmos DB database",
- "namingConvention": "cosmos-"
- },
- "Microsoft.EventGrid/domains": {
- "friendlyName": "Event Grid domain",
- "namingConvention": "evgd-"
- },
- "Microsoft.EventGrid/domains/topics": {
- "friendlyName": "Event Grid topic",
- "namingConvention": "evgt-"
- },
- "Microsoft.EventGrid/eventSubscriptions": {
- "friendlyName": "Event Grid subscriptions",
- "namingConvention": "evgs-"
- },
- "Microsoft.EventHub/namespaces": {
- "friendlyName": "Event Hubs namespace",
- "namingConvention": "evhns-"
- },
- "Microsoft.EventHub/namespaces/eventHubs": {
- "friendlyName": "Event hub",
- "namingConvention": "evh-"
- },
- "Microsoft.HDInsight/clusters": {
- "friendlyName": [
- "HDInsight - Hadoop cluster",
- "HDInsight - Kafka cluster",
- "HDInsight - Spark cluster",
- "HDInsight - Storm cluster",
- "HDInsight - ML Services cluster",
- "HDInsight - HBase cluster"
- ],
- "namingConvention": [
- "hadoop-",
- "kafka-",
- "spark-",
- "storm-",
- "mls-",
- "hbase-"
- ]
- },
- "Microsoft.HybridCompute/machines": {
- "friendlyName": "Azure Arc enabled server",
- "namingConvention": "arcs-"
- },
- "Microsoft.Insights/actionGroups": {
- "friendlyName": "Azure Monitor action group",
- "namingConvention": "ag-"
- },
- "Microsoft.Insights/components": {
- "friendlyName": "Application Insights",
- "namingConvention": "appi-"
- },
- "Microsoft.KeyVault/vaults": {
- "friendlyName": "Key vault",
- "namingConvention": "kv-"
- },
- "Microsoft.Kubernetes/connectedClusters": {
- "friendlyName": "Azure Arc enabled Kubernetes cluster",
- "namingConvention": "arck"
- },
- "Microsoft.Kusto/clusters": {
- "friendlyName": "Azure Data Explorer cluster",
- "namingConvention": "dec"
- },
- "Microsoft.Kusto/clusters/databases": {
- "friendlyName": "Azure Data Explorer cluster database",
- "namingConvention": "dedb"
- },
- "Microsoft.Logic/integrationAccounts": {
- "friendlyName": "Integration account",
- "namingConvention": "ia-"
- },
- "Microsoft.Logic/workflows": {
- "friendlyName": "Logic apps",
- "namingConvention": "logic-"
- },
- "Microsoft.MachineLearningServices/workspaces": {
- "friendlyName": "Azure Machine Learning workspace",
- "namingConvention": "mlw-"
- },
- "Microsoft.ManagedIdentity/userAssignedIdentities": {
- "friendlyName": "Managed Identity",
- "namingConvention": "id-"
- },
- "Microsoft.Management/managementGroups": {
- "friendlyName": "Management group",
- "namingConvention": "mg-"
- },
- "Microsoft.Migrate/assessmentProjects": {
- "friendlyName": "Azure Migrate project",
- "namingConvention": "migr-"
- },
- "Microsoft.Network/applicationGateways": {
- "friendlyName": "Application gateway",
- "namingConvention": "agw-"
- },
- "Microsoft.Network/applicationSecurityGroups": {
- "friendlyName": "Application security group (ASG)",
- "namingConvention": "asg-"
- },
- "Microsoft.Network/azureFirewalls": {
- "friendlyName": "Firewall",
- "namingConvention": "afw-"
- },
- "Microsoft.Network/bastionHosts": {
- "friendlyName": "Bastion",
- "namingConvention": "bas-"
- },
- "Microsoft.Network/connections": {
- "friendlyName": "Connections",
- "namingConvention": "con-"
- },
- "Microsoft.Network/dnsZones": {
- "friendlyName": "DNS",
- "namingConvention": "dnsz-"
- },
- "Microsoft.Network/expressRouteCircuits": {
- "friendlyName": "ExpressRoute circuit",
- "namingConvention": "erc-"
- },
- "Microsoft.Network/firewallPolicies": {
- "friendlyName": [
- "Web Application Firewall (WAF) policy",
- "Firewall policy"
- ],
- "namingConvention": [
- "waf",
- "afwp-"
- ]
- },
- "Microsoft.Network/firewallPolicies/ruleGroups": {
- "friendlyName": "Web Application Firewall (WAF) policy rule group",
- "namingConvention": "wafrg"
- },
- "Microsoft.Network/frontDoors": {
- "friendlyName": "Front Door instance",
- "namingConvention": "fd-"
- },
- "Microsoft.Network/frontdoorWebApplicationFirewallPolicies": {
- "friendlyName": "Front Door firewall policy",
- "namingConvention": "fdfp-"
- },
- "Microsoft.Network/loadBalancers": {
- "friendlyName": [
- "Load balancer (external)",
- "Load balancer (internal)"
- ],
- "namingConvention": [
- "lbe-",
- "lbi-"
- ]
- },
- "Microsoft.Network/loadBalancers/inboundNatRules": {
- "friendlyName": "Load balancer rule",
- "namingConvention": "rule-"
- },
- "Microsoft.Network/localNetworkGateways": {
- "friendlyName": "Local network gateway",
- "namingConvention": "lgw-"
- },
- "Microsoft.Network/natGateways": {
- "friendlyName": "NAT gateway",
- "namingConvention": "ng-"
- },
- "Microsoft.Network/networkInterfaces": {
- "friendlyName": "Network interface (NIC)",
- "namingConvention": "nic-"
- },
- "Microsoft.Network/networkSecurityGroups": {
- "friendlyName": "Network security group (NSG)",
- "namingConvention": "nsg-"
- },
- "Microsoft.Network/networkSecurityGroups/securityRules": {
- "friendlyName": "Network security group (NSG) security rules",
- "namingConvention": "nsgsr-"
- },
- "Microsoft.Network/networkWatchers": {
- "friendlyName": "Network Watcher",
- "namingConvention": "nw-"
- },
- "Microsoft.Network/privateDnsZones": {
- "friendlyName": "DNS zone",
- "namingConvention": "pdnsz-"
- },
- "Microsoft.Network/privateLinkServices": {
- "friendlyName": "Private Link",
- "namingConvention": "pl-"
- },
- "Microsoft.Network/publicIPAddresses": {
- "friendlyName": "Public IP address",
- "namingConvention": "pip-"
- },
- "Microsoft.Network/publicIPPrefixes": {
- "friendlyName": "Public IP address prefix",
- "namingConvention": "ippre-"
- },
- "Microsoft.Network/routeFilters": {
- "friendlyName": "Route filter",
- "namingConvention": "rf-"
- },
- "Microsoft.Network/routeTables": {
- "friendlyName": "Route table",
- "namingConvention": "rt-"
- },
- "Microsoft.Network/routeTables/routes": {
- "friendlyName": "User defined route (UDR)",
- "namingConvention": "udr-"
- },
- "Microsoft.Network/trafficManagerProfiles": {
- "friendlyName": "Traffic Manager profile",
- "namingConvention": "traf-"
- },
- "Microsoft.Network/virtualNetworkGateways": {
- "friendlyName": "Virtual network gateway",
- "namingConvention": "vgw-"
- },
- "Microsoft.Network/virtualNetworks": {
- "friendlyName": "Virtual network",
- "namingConvention": "vnet-"
- },
- "Microsoft.Network/virtualNetworks/subnets": {
- "friendlyName": "Virtual network subnet",
- "namingConvention": "snet-"
- },
- "Microsoft.Network/virtualNetworks/virtualNetworkPeerings": {
- "friendlyName": "Virtual network peering",
- "namingConvention": "peer-"
- },
- "Microsoft.Network/virtualWans": {
- "friendlyName": "Virtual WAN",
- "namingConvention": "vwan-"
- },
- "Microsoft.Network/vpnGateways": {
- "friendlyName": "VPN Gateway",
- "namingConvention": "vpng-"
- },
- "Microsoft.Network/vpnGateways/vpnConnections": {
- "friendlyName": "VPN connection",
- "namingConvention": "vcn-"
- },
- "Microsoft.Network/vpnGateways/vpnSites": {
- "friendlyName": "VPN site",
- "namingConvention": "vst-"
- },
- "Microsoft.NotificationHubs/namespaces": {
- "friendlyName": "Notification Hubs namespace",
- "namingConvention": "ntfns-"
- },
- "Microsoft.NotificationHubs/namespaces/notificationHubs": {
- "friendlyName": "Notification Hubs",
- "namingConvention": "ntf-"
- },
- "Microsoft.OperationalInsights/workspaces": {
- "friendlyName": "Log Analytics workspace",
- "namingConvention": "log-"
- },
- "Microsoft.PowerBIDedicated/capacities": {
- "friendlyName": "Power BI Embedded",
- "namingConvention": "pbi-"
- },
- "Microsoft.Purview/accounts": {
- "friendlyName": "Azure Purview instance",
- "namingConvention": "pview-"
- },
- "Microsoft.RecoveryServices/vaults": {
- "friendlyName": "Recovery Services vault",
- "namingConvention": "rsv-"
- },
- "Microsoft.RecoveryServices/vaults/backupPolicies": {
- "friendlyName": "Recovery Services vault backup policy",
- "namingConvention": "rsvbp-"
- },
- "Microsoft.Resources/resourceGroups": {
- "friendlyName": "Resource group",
- "namingConvention": "rg-"
- },
- "Microsoft.Search/searchServices": {
- "friendlyName": "Azure Cognitive Search",
- "namingConvention": "srch-"
- },
- "Microsoft.ServiceBus/namespaces": {
- "friendlyName": "Service Bus",
- "namingConvention": "sb-"
- },
- "Microsoft.ServiceBus/namespaces/queues": {
- "friendlyName": "Service Bus queue",
- "namingConvention": "sbq-"
- },
- "Microsoft.ServiceBus/namespaces/topics": {
- "friendlyName": "Service Bus topic",
- "namingConvention": "sbt-"
- },
- "Microsoft.serviceEndPointPolicies": {
- "friendlyName": "Service endpoint",
- "namingConvention": "se-"
- },
- "Microsoft.ServiceFabric/clusters": {
- "friendlyName": "Service Fabric cluster",
- "namingConvention": "sf-"
- },
- "Microsoft.SignalRService/SignalR": {
- "friendlyName": "SignalR",
- "namingConvention": "sigr"
- },
- "Microsoft.Sql/managedInstances": {
- "friendlyName": "SQL Managed Instance",
- "namingConvention": "sqlmi-"
- },
- "Microsoft.Sql/servers": {
- "friendlyName": [
- "Azure SQL Data Warehouse",
- "Azure SQL Database server"
- ],
- "namingConvention": [
- "sqldw-",
- "sql-"
- ]
- },
- "Microsoft.Sql/servers/databases": {
- "friendlyName": [
- "SQL Server Stretch Database",
- "Azure SQL database"
- ],
- "namingConvention": [
- "sqlstrdb-",
- "sqldb-"
- ]
- },
- "Microsoft.Storage/storageAccounts": {
- "friendlyName": [
- "Storage account",
- "VM storage account"
- ],
- "namingConvention": [
- "st",
- "stvm"
- ]
- },
- "Microsoft.StorSimple/managers": {
- "friendlyName": "Azure StorSimple",
- "namingConvention": "ssimp"
- },
- "Microsoft.StreamAnalytics/cluster": {
- "friendlyName": "Azure Stream Analytics",
- "namingConvention": "asa-"
- },
- "Microsoft.Synapse/workspaces": {
- "friendlyName": [
- "Azure Synapse Analytics Workspaces",
- "Azure Synapse Analytics"
- ],
- "namingConvention": [
- "synw",
- "syn"
- ]
- },
- "Microsoft.Synapse/workspaces/sqlPools": {
- "friendlyName": [
- "Azure Synapse Analytics Spark Pool",
- "Azure Synapse Analytics SQL Dedicated Pool"
- ],
- "namingConvention": [
- "synsp",
- "syndp"
- ]
- },
- "Microsoft.TimeSeriesInsights/environments": {
- "friendlyName": "Time Series Insights environment",
- "namingConvention": "tsi-"
- },
- "Microsoft.Web/serverFarms": {
- "friendlyName": "App Service plan",
- "namingConvention": "plan-"
- },
- "Microsoft.Web/sites": {
- "friendlyName": [
- "Web app",
- "Function app",
- "App Service environment"
- ],
- "namingConvention": [
- "app-",
- "func-",
- "ase-"
- ]
- },
- "Microsoft.Web/staticSites": {
- "friendlyName": "Static web app",
- "namingConvention": "stapp-"
- }
- }
-'@
- $htCAFNamingConvention = $JSONcafResourceNaming | ConvertFrom-Json
-
- $resourcesSubscriptionResultGroupedByType = $resourcesSubscriptionResult | Group-Object -Property type
- foreach ($entry in $resourcesSubscriptionResultGroupedByType) {
-
- if ($htCAFNamingConvention.($entry.Name)) {
- $doCAFResourceNamingCheck = $true
- $namingConvention = $htCAFNamingConvention.($entry.Name).namingConvention
- $namingConventionFriendlyName = $htCAFNamingConvention.($entry.Name).friendlyName
- }
- else {
- $doCAFResourceNamingCheck = $false
- $namingConvention = 'n/a'
- $namingConventionFriendlyName = 'n/a'
- }
-
- foreach ($resource in ($entry.Group)) {
-
- if ($doCAFResourceNamingCheck) {
- $cafResourceNamingCheck = 'failed'
- $applicableNaming = $namingConvention -join "$CsvDelimiterOpposite "
- foreach ($naming in $namingConvention) {
- if (($resource.name).StartsWith($naming, 'CurrentCultureIgnoreCase')) {
- $cafResourceNamingCheck = 'passed'
- #$applicableNaming = $naming
- }
- }
- }
- else {
- $cafResourceNamingCheck = 'n/a'
- $applicableNaming = 'n/a'
- }
- $null = $script:resourcesIdsAll.Add([PSCustomObject]@{
- subscriptionId = $scopeId
- mgPath = $childMgMgPath
- type = ($resource.type).ToLower()
- id = ($resource.Id).ToLower()
- name = ($resource.name).ToLower()
- location = ($resource.location).ToLower()
- tags = ($resource.tags)
- createdTime = ($resource.createdTime)
- changedTime = ($resource.changedTime)
- cafResourceNamingResult = $cafResourceNamingCheck
- cafResourceNaming = $applicableNaming
- cafResourceNamingFriendlyName = $namingConventionFriendlyName -join "$CSVDelimiterOpposite "
- })
-
- if ($resource.identity.userAssignedIdentities) {
- $resource.identity.userAssignedIdentities.psobject.properties | ForEach-Object {
- if ((-not [string]::IsNullOrEmpty($resource.Id)) -and (-not [string]::IsNullOrEmpty($_.Value.principalId))) {
- $hlp = ($_.Name.split('/'))
- $hlpMiSubId = $hlp[2]
- if ($scopeId -eq $hlpMiSubId) {
- $miCrossSubscription = $false
- }
- else {
- $miCrossSubscription = $true
- }
- $null = $script:arrayUserAssignedIdentities4Resources.Add([PSCustomObject]@{
- resourceId = $resource.Id
- resourceName = $resource.name
- resourceMgPath = $childMgMgPath
- resourceSubscriptionName = $scopeDisplayName
- resourceSubscriptionId = $scopeId
- resourceResourceGroupName = ($resource.Id -split ('/'))[4]
- resourceType = $resource.type
- resourceLocation = $resource.location
- miPrincipalId = $_.Value.principalId
- miClientId = $_.Value.clientId
- miMgPath = $htSubscriptionsMgPath.($hlpMiSubId).pathDelimited
- miSubscriptionName = $htSubscriptionsMgPath.($hlpMiSubId).DisplayName
- miSubscriptionId = $hlpMiSubId
- miResourceGroupName = $hlp[4]
- miResourceId = $_.Name
- miResourceName = $_.Name -replace '.*/'
- miCrossSubscription = $miCrossSubscription
- })
- }
- }
- }
- }
- }
- $endSubResourceIdsThis = Get-Date
- $null = $script:arraySubResourcesAddArrayDuration.Add([PSCustomObject]@{
- sub = $scopeId
- DurationSec = (New-TimeSpan -Start $startSubResourceIdsThis -End $endSubResourceIdsThis).TotalSeconds
- })
-
-
- #resourceTags
- $script:htSubscriptionTagList.($scopeId) = @{}
- $script:htSubscriptionTagList.($scopeId).Resource = @{}
- foreach ($tags in ($resourcesSubscriptionResult.where( { $_.Tags -and -not [String]::IsNullOrWhiteSpace($_.Tags) } )).Tags) {
- foreach ($tagName in $tags.PSObject.Properties.Name) {
- #resource
- if ($htSubscriptionTagList.($scopeId).Resource.ContainsKey($tagName)) {
- $script:htSubscriptionTagList.($scopeId).Resource."$tagName" += 1
- }
- else {
- $script:htSubscriptionTagList.($scopeId).Resource."$tagName" = 1
- }
-
- #resourceAll
- if ($htAllTagList.Resource.ContainsKey($tagName)) {
- $script:htAllTagList.Resource."$tagName" += 1
- }
- else {
- $script:htAllTagList.Resource."$tagName" = 1
- }
-
- #all
- if ($htAllTagList.AllScopes.ContainsKey($tagName)) {
- $script:htAllTagList.AllScopes."$tagName" += 1
- }
- else {
- $script:htAllTagList.AllScopes."$tagName" = 1
- }
- }
- }
-}
-$funcDataCollectionResources = $function:dataCollectionResources.ToString()
-
-function dataCollectionResourceGroups {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $subscriptionQuotaId
- )
-
- #https://management.azure.com/subscriptions/{subscriptionId}/resourcegroups?api-version=2020-06-01
- $currentTask = "Getting ResourceGroups for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/resourcegroups?api-version=2021-04-01"
- $method = 'GET'
- $resourceGroupsSubscriptionResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $null = $script:resourceGroupsAll.Add([PSCustomObject]@{
- subscriptionId = $scopeId
- count_ = ($resourceGroupsSubscriptionResult).count
- })
-
- #resourceGroupTags
- if ($azAPICallConf['htParameters'].NoResources -eq $true) {
- $script:htSubscriptionTagList.($scopeId) = @{}
- }
-
- $script:htSubscriptionTagList.($scopeId).ResourceGroup = @{}
- foreach ($tags in ($resourceGroupsSubscriptionResult.where( { $_.Tags -and -not [String]::IsNullOrWhiteSpace($_.Tags) } )).Tags) {
- foreach ($tagName in $tags.PSObject.Properties.Name) {
-
- #resource
- if ($htSubscriptionTagList.($scopeId).ResourceGroup.ContainsKey($tagName)) {
- $script:htSubscriptionTagList.($scopeId).ResourceGroup."$tagName" += 1
- }
- else {
- $script:htSubscriptionTagList.($scopeId).ResourceGroup."$tagName" = 1
- }
-
- #resourceAll
- if ($htAllTagList.ResourceGroup.ContainsKey($tagName)) {
- $script:htAllTagList.ResourceGroup."$tagName" += 1
- }
- else {
- $script:htAllTagList.ResourceGroup."$tagName" = 1
- }
-
- #all
- if ($htAllTagList.AllScopes.ContainsKey($tagName)) {
- $script:htAllTagList.AllScopes."$tagName" += 1
- }
- else {
- $script:htAllTagList.AllScopes."$tagName" = 1
- }
- }
- }
-}
-$funcDataCollectionResourceGroups = $function:dataCollectionResourceGroups.ToString()
-
-function dataCollectionResourceProviders {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayname,
- $subscriptionQuotaId
- )
-
- ($script:htResourceProvidersAll).($scopeId) = @{}
- $currentTask = "Getting ResourceProviders for Subscription: '$($scopeDisplayname)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers?api-version=2019-10-01"
- $method = 'GET'
- $resProvResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- ($script:htResourceProvidersAll).($scopeId).Providers = $resProvResult | Select-Object namespace, registrationState
-}
-$funcDataCollectionResourceProviders = $function:dataCollectionResourceProviders.ToString()
-
-function dataCollectionFeatures {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayname,
- [object]$MgParentNameChain,
- $subscriptionQuotaId
- )
-
- $currentTask = "Getting Features for Subscription: '$($scopeDisplayname)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Features/features?api-version=2021-07-01"
- $method = 'GET'
- $featuresResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $featuresResultRegistered = $featuresResult.where({ $_.properties.state -eq 'Registered' })
-
- if ($featuresResultRegistered.Count -gt 0) {
- foreach ($registeredFeature in $featuresResultRegistered) {
- $null = $script:arrayFeaturesAll.Add([PSCustomObject]@{
- subscriptionId = $registeredFeature.id.split('/')[2]
- mgPathArray = $MgParentNameChain
- mgPath = ($MgParentNameChain -join ',')
- feature = $registeredFeature.name
- })
- }
- }
-}
-$funcDataCollectionFeatures = $function:dataCollectionFeatures.ToString()
-
-function dataCollectionResourceLocks {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayname,
- $subscriptionQuotaId
- )
-
- $currentTask = "Getting ResourceLocks for Subscription: '$($scopeDisplayname)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/locks?api-version=2016-09-01"
- $method = 'GET'
- $requestSubscriptionResourceLocks = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $requestSubscriptionResourceLocksCount = ($requestSubscriptionResourceLocks).Count
- if ($requestSubscriptionResourceLocksCount -gt 0) {
- $htTemp = @{}
- $locksAnyLockSubscriptionCount = 0
- $locksCannotDeleteSubscriptionCount = 0
- $locksReadOnlySubscriptionCount = 0
- $arrayResourceGroupsAnyLock = [System.Collections.ArrayList]@()
- $arrayResourceGroupsCannotDeleteLock = [System.Collections.ArrayList]@()
- $arrayResourceGroupsReadOnlyLock = [System.Collections.ArrayList]@()
- $arrayResourcesAnyLock = [System.Collections.ArrayList]@()
- $arrayResourcesCannotDeleteLock = [System.Collections.ArrayList]@()
- $arrayResourcesReadOnlyLock = [System.Collections.ArrayList]@()
- foreach ($requestSubscriptionResourceLock in $requestSubscriptionResourceLocks) {
-
- $splitRequestSubscriptionResourceLockId = ($requestSubscriptionResourceLock.Id).Split('/')
- switch (($splitRequestSubscriptionResourceLockId).Count - 1) {
- #subLock
- 6 {
- $locksAnyLockSubscriptionCount++
- if ($requestSubscriptionResourceLock.properties.level -eq 'CanNotDelete') {
- $locksCannotDeleteSubscriptionCount++
- }
- if ($requestSubscriptionResourceLock.properties.level -eq 'ReadOnly') {
- $locksReadOnlySubscriptionCount++
- }
- }
- #rgLock
- 8 {
- $resourceGroupName = $splitRequestSubscriptionResourceLockId[0..4] -join '/'
- $null = $arrayResourceGroupsAnyLock.Add([PSCustomObject]@{
- rg = $resourceGroupName
- })
- if ($requestSubscriptionResourceLock.properties.level -eq 'CanNotDelete') {
- $null = $arrayResourceGroupsCannotDeleteLock.Add([PSCustomObject]@{
- rg = $resourceGroupName
- })
- }
- if ($requestSubscriptionResourceLock.properties.level -eq 'ReadOnly') {
- $null = $arrayResourceGroupsReadOnlyLock.Add([PSCustomObject]@{
- rg = $resourceGroupName
- })
- }
- }
- #resLock
- 12 {
- $resourceId = $splitRequestSubscriptionResourceLockId[0..8] -join '/'
- $null = $arrayResourcesAnyLock.Add([PSCustomObject]@{
- res = $resourceId
- })
- if ($requestSubscriptionResourceLock.properties.level -eq 'CanNotDelete') {
- $null = $arrayResourcesCannotDeleteLock.Add([PSCustomObject]@{
- res = $resourceId
- })
- }
- if ($requestSubscriptionResourceLock.properties.level -eq 'ReadOnly') {
- $null = $arrayResourcesReadOnlyLock.Add([PSCustomObject]@{
- res = $resourceId
- })
- }
- }
- }
- }
-
- $htTemp.SubscriptionLocksCannotDeleteCount = $locksCannotDeleteSubscriptionCount
- $htTemp.SubscriptionLocksReadOnlyCount = $locksReadOnlySubscriptionCount
-
- #resourceGroups
- $htTemp.ResourceGroupsLocksCannotDeleteCount = $arrayResourceGroupsCannotDeleteLock.Count
- $htTemp.ResourceGroupsLocksCannotDelete = $arrayResourceGroupsCannotDeleteLock
-
- $htTemp.ResourceGroupsLocksReadOnlyCount = $arrayResourceGroupsReadOnlyLock.Count
- $htTemp.ResourceGroupsLocksReadOnly = $arrayResourceGroupsReadOnlyLock
-
- #resources
- $htTemp.ResourcesLocksCannotDeleteCount = $arrayResourcesCannotDeleteLock.Count
- $htTemp.ResourcesLocksCannotDelete = $arrayResourcesCannotDeleteLock
-
- $htTemp.ResourcesLocksReadOnlyCount = $arrayResourcesReadOnlyLock.Count
- $htTemp.ResourcesLocksReadOnly = $arrayResourcesReadOnlyLock
-
- $script:htResourceLocks.($scopeId) = $htTemp
- }
-}
-$funcDataCollectionResourceLocks = $function:dataCollectionResourceLocks.ToString()
-
-function dataCollectionTags {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $subscriptionQuotaId
- )
-
- $currentTask = "Getting Tags for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Resources/tags/default?api-version=2020-06-01"
- $method = 'GET'
- $requestSubscriptionTags = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -listenOn 'Content' -caller 'CustomDataCollection'
-
- $script:htSubscriptionTagList.($scopeId).Subscription = @{}
- if ($requestSubscriptionTags.properties.tags) {
- $subscriptionTags = @()
- ($script:htSubscriptionTags).($scopeId) = @{}
- foreach ($tag in ($requestSubscriptionTags.properties.tags).PSObject.Properties) {
- $subscriptionTags += "$($tag.Name)/$($tag.Value)"
-
- ($script:htSubscriptionTags).($scopeId).($tag.Name) = $tag.Value
- $tagName = $tag.Name
-
- #subscription
- if ($htSubscriptionTagList.($scopeId).Subscription.ContainsKey($tagName)) {
- $script:htSubscriptionTagList.($scopeId).Subscription."$tagName" += 1
- }
- else {
- $script:htSubscriptionTagList.($scopeId).Subscription."$tagName" = 1
- }
-
- #subscriptionAll
- if ($htAllTagList.Subscription.ContainsKey($tagName)) {
- $script:htAllTagList.Subscription."$tagName" += 1
- }
- else {
- $script:htAllTagList.Subscription."$tagName" = 1
- }
-
- #all
- if ($htAllTagList.AllScopes.ContainsKey($tagName)) {
- $script:htAllTagList.AllScopes."$tagName" += 1
- }
- else {
- $script:htAllTagList.AllScopes."$tagName" = 1
- }
-
- }
- $subscriptionTagsCount = ($subscriptionTags).Count
- $subscriptionTags = $subscriptionTags -join "$CsvDelimiterOpposite "
- }
- else {
- $subscriptionTagsCount = 0
- $subscriptionTags = 'none'
- }
- $htSubscriptionTagsReturn = @{}
- $htSubscriptionTagsReturn.subscriptionTagsCount = $subscriptionTagsCount
- $htSubscriptionTagsReturn.subscriptionTags = $subscriptionTags
- return $htSubscriptionTagsReturn
-}
-$funcDataCollectionTags = $function:dataCollectionTags.ToString()
-
-function dataCollectionPolicyComplianceStates {
- [CmdletBinding()]Param(
- [string]$TargetMgOrSub,
- [string]$scopeId,
- [string]$scopeDisplayName,
- $subscriptionQuotaId
- )
-
-
- if ($TargetMgOrSub -eq 'Sub') {
- $currentTask = "Getting Policy Compliance for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.PolicyInsights/policyStates/latest/summarize?api-version=2019-10-01"
- }
- if ($TargetMgOrSub -eq 'MG') {
- $currentTask = "Getting Policy Compliance for Management Group: '$($scopeDisplayName)' ('$scopeId')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($scopeId)/providers/Microsoft.PolicyInsights/policyStates/latest/summarize?api-version=2019-10-01"
- }
- $method = 'POST'
- $policyComplianceResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- if ($policyComplianceResult -eq 'ResponseTooLarge') {
- if ($TargetMgOrSub -eq 'Sub') {
- $script:htCachePolicyComplianceResponseTooLargeSUB.($scopeId) = @{}
- }
- if ($TargetMgOrSub -eq 'MG') {
- $script:htCachePolicyComplianceResponseTooLargeMG.($scopeId) = @{}
- }
- }
- elseif ($policyComplianceResult -eq 'DisallowedProvider') {
- #nothing to do
- }
- else {
- if ($TargetMgOrSub -eq 'Sub') { ($script:htCachePolicyComplianceSUB).($scopeId) = @{} }
- if ($TargetMgOrSub -eq 'MG') { ($script:htCachePolicyComplianceMG).($scopeId) = @{} }
- foreach ($policyAssignment in $policyComplianceResult.policyassignments | Sort-Object -Property policyAssignmentId) {
- $policyAssignmentIdToLower = ($policyAssignment.policyAssignmentId).ToLower()
- if ($TargetMgOrSub -eq 'Sub') { ($script:htCachePolicyComplianceSUB).($scopeId).($policyAssignmentIdToLower) = @{} }
- if ($TargetMgOrSub -eq 'MG') { ($script:htCachePolicyComplianceMG).($scopeId).($policyAssignmentIdToLower) = @{} }
- foreach ($policyComplianceState in $policyAssignment.results.policydetails) {
- if ($policyComplianceState.ComplianceState -eq 'compliant') {
- if ($TargetMgOrSub -eq 'Sub') { ($script:htCachePolicyComplianceSUB).($scopeId).($policyAssignmentIdToLower).CompliantPolicies = $policyComplianceState.count }
- if ($TargetMgOrSub -eq 'MG') { ($script:htCachePolicyComplianceMG).($scopeId).($policyAssignmentIdToLower).CompliantPolicies = $policyComplianceState.count }
- }
- if ($policyComplianceState.ComplianceState -eq 'noncompliant') {
- if ($TargetMgOrSub -eq 'Sub') { ($script:htCachePolicyComplianceSUB).($scopeId).($policyAssignmentIdToLower).NonCompliantPolicies = $policyComplianceState.count }
- if ($TargetMgOrSub -eq 'MG') { ($script:htCachePolicyComplianceMG).($scopeId).($policyAssignmentIdToLower).NonCompliantPolicies = $policyComplianceState.count }
- }
- }
-
- foreach ($resourceComplianceState in $policyAssignment.results.resourcedetails) {
- if ($resourceComplianceState.ComplianceState -eq 'compliant') {
- if ($TargetMgOrSub -eq 'Sub') { ($script:htCachePolicyComplianceSUB).($scopeId).($policyAssignmentIdToLower).CompliantResources = $resourceComplianceState.count }
- if ($TargetMgOrSub -eq 'MG') { ($script:htCachePolicyComplianceMG).($scopeId).($policyAssignmentIdToLower).CompliantResources = $resourceComplianceState.count }
-
- }
- if ($resourceComplianceState.ComplianceState -eq 'nonCompliant') {
- if ($TargetMgOrSub -eq 'Sub') { ($script:htCachePolicyComplianceSUB).($scopeId).($policyAssignmentIdToLower).NonCompliantResources = $resourceComplianceState.count }
- if ($TargetMgOrSub -eq 'MG') { ($script:htCachePolicyComplianceMG).($scopeId).($policyAssignmentIdToLower).NonCompliantResources = $resourceComplianceState.count }
-
- }
- if ($resourceComplianceState.ComplianceState -eq 'conflict') {
- if ($TargetMgOrSub -eq 'Sub') { ($script:htCachePolicyComplianceSUB).($scopeId).($policyAssignmentIdToLower).ConflictingResources = $resourceComplianceState.count }
- if ($TargetMgOrSub -eq 'MG') { ($script:htCachePolicyComplianceMG).($scopeId).($policyAssignmentIdToLower).ConflictingResources = $resourceComplianceState.count }
- }
- }
- }
- }
-}
-$funcDataCollectionPolicyComplianceStates = $function:dataCollectionPolicyComplianceStates.ToString()
-
-function dataCollectionASCSecureScoreSub {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $subscriptionQuotaId
- )
-
- if ($azAPICallConf['htParameters'].NoMDfCSecureScore -eq $false) {
- $currentTask = "Getting Microsoft Defender for Cloud Secure Score for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Security/securescores?api-version=2020-01-01"
- $method = 'GET'
- $subASCSecureScoreResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- if ($subASCSecureScoreResult -ne 'DisallowedProvider') {
- $subASCSecureScoreResultASCScore = ($subASCSecureScoreResult.where({ $_.name -eq 'ascScore' }))
- if ($subASCSecureScoreResultASCScore.count -gt 0) {
- $secureScorePercentageRounded = [math]::Round(($subASCSecureScoreResultASCScore.properties.score.current / $subASCSecureScoreResultASCScore.properties.score.max * 100), 2)
- $subscriptionASCSecureScore = "$($secureScorePercentageRounded)% ($($subASCSecureScoreResultASCScore.properties.score.current) of $($subASCSecureScoreResultASCScore.properties.score.max) points)"
- }
- else {
- $subscriptionASCSecureScore = 'n/a'
- }
- }
- else {
- $subscriptionASCSecureScore = 'n/a'
- }
-
- }
- else {
- $subscriptionASCSecureScore = "excluded (-NoMDfCSecureScore $($azAPICallConf['htParameters'].NoMDfCSecureScore))"
- }
- return $subscriptionASCSecureScore
-}
-$funcDataCollectionASCSecureScoreSub = $function:dataCollectionASCSecureScoreSub.ToString()
-
-function dataCollectionBluePrintDefinitionsMG {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $hierarchyLevel,
- $mgParentId,
- $mgParentName,
- $mgAscSecureScoreResult
- )
-
- $currentTask = "Getting Blueprint definitions for Management Group: '$($scopeDisplayName)' ('$scopeId')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($scopeId)/providers/Microsoft.Blueprint/blueprints?api-version=2018-11-01-preview"
- $method = 'GET'
- $scopeBlueprintDefinitionResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $addRowToTableDone = $false
- if (($scopeBlueprintDefinitionResult).count -gt 0) {
- foreach ($blueprint in $scopeBlueprintDefinitionResult) {
-
- if (-not $($htCacheDefinitionsBlueprint).($blueprint.Id)) {
- ($script:htCacheDefinitionsBlueprint).($blueprint.Id) = @{}
- }
-
- $blueprintName = $blueprint.name
- $blueprintId = $blueprint.Id
- $blueprintDisplayName = $blueprint.properties.displayName
- $blueprintDescription = $blueprint.properties.description
- $blueprintScoped = "/providers/Microsoft.Management/managementGroups/$($scopeId)"
-
- $addRowToTableDone = $true
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $scopeDisplayName `
- -mgId $scopeId `
- -mgParentId $mgParentId `
- -mgParentName $mgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -BlueprintName $blueprintName `
- -BlueprintId $blueprintId `
- -BlueprintDisplayName $blueprintDisplayName `
- -BlueprintDescription $blueprintDescription `
- -BlueprintScoped $blueprintScoped
- }
- }
-
- $returnObject = @{}
- if ($addRowToTableDone) {
- $returnObject.'addRowToTableDone' = @{}
- }
- return $returnObject
-}
-$funcDataCollectionBluePrintDefinitionsMG = $function:dataCollectionBluePrintDefinitionsMG.ToString()
-
-function dataCollectionBluePrintDefinitionsSub {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $hierarchyLevel,
- $childMgDisplayName,
- $childMgId,
- $childMgParentId,
- $childMgParentName,
- $mgAscSecureScoreResult,
- $subscriptionQuotaId,
- $subscriptionState,
- $subscriptionASCSecureScore,
- $subscriptionTags,
- $subscriptionTagsCount
- )
-
- $currentTask = "Getting Blueprint definitions for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Blueprint/blueprints?api-version=2018-11-01-preview"
- $method = 'GET'
- $scopeBlueprintDefinitionResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $addRowToTableDone = $false
- if ($scopeBlueprintDefinitionResult -ne 'DisallowedProvider') {
- if (($scopeBlueprintDefinitionResult).count -gt 0) {
- foreach ($blueprint in $scopeBlueprintDefinitionResult) {
-
- if (-not $($htCacheDefinitionsBlueprint).($blueprint.Id)) {
- ($script:htCacheDefinitionsBlueprint).($blueprint.Id) = @{}
- }
-
- $blueprintName = $blueprint.name
- $blueprintId = $blueprint.Id
- $blueprintDisplayName = $blueprint.properties.displayName
- $blueprintDescription = $blueprint.properties.description
- $blueprintScoped = "/subscriptions/$($scopeId)"
-
- $addRowToTableDone = $true
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $childMgDisplayName `
- -mgId $childMgId `
- -mgParentId $childMgParentId `
- -mgParentName $childMgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Subscription $scopeDisplayName `
- -SubscriptionId $scopeId `
- -SubscriptionQuotaId $subscriptionQuotaId `
- -SubscriptionState $subscriptionState `
- -SubscriptionASCSecureScore $subscriptionASCSecureScore `
- -SubscriptionTags $subscriptionTags `
- -SubscriptionTagsCount $subscriptionTagsCount `
- -BlueprintName $blueprintName `
- -BlueprintId $blueprintId `
- -BlueprintDisplayName $blueprintDisplayName `
- -BlueprintDescription $blueprintDescription `
- -BlueprintScoped $blueprintScoped
- }
- }
- }
-
- $returnObject = @{}
- if ($addRowToTableDone) {
- $returnObject.'addRowToTableDone' = @{}
- }
- return $returnObject
-}
-$funcDataCollectionBluePrintDefinitionsSub = $function:dataCollectionBluePrintDefinitionsSub.ToString()
-
-function dataCollectionBluePrintAssignmentsSub {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $hierarchyLevel,
- $childMgDisplayName,
- $childMgId,
- $childMgParentId,
- $childMgParentName,
- $mgAscSecureScoreResult,
- $subscriptionQuotaId,
- $subscriptionState,
- $subscriptionASCSecureScore,
- $subscriptionTags,
- $subscriptionTagsCount
- )
-
- $currentTask = "Getting Blueprint assignments for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Blueprint/blueprintAssignments?api-version=2018-11-01-preview"
- $method = 'GET'
- $subscriptionBlueprintAssignmentsResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $addRowToTableDone = $false
- if ($subscriptionBlueprintAssignmentsResult -ne 'DisallowedProvider') {
- if (($subscriptionBlueprintAssignmentsResult).count -gt 0) {
- foreach ($subscriptionBlueprintAssignment in $subscriptionBlueprintAssignmentsResult) {
-
- if (-not ($htCacheAssignmentsBlueprint).($subscriptionBlueprintAssignment.Id)) {
- ($script:htCacheAssignmentsBlueprint).($subscriptionBlueprintAssignment.Id) = @{}
- ($script:htCacheAssignmentsBlueprint).($subscriptionBlueprintAssignment.Id) = $subscriptionBlueprintAssignment
- }
-
- if (($subscriptionBlueprintAssignment.properties.blueprintId) -like '/subscriptions/*') {
- $blueprintScope = $subscriptionBlueprintAssignment.properties.blueprintId -replace '/providers/Microsoft.Blueprint/blueprints/.*', ''
- $blueprintName = $subscriptionBlueprintAssignment.properties.blueprintId -replace '.*/blueprints/', '' -replace '/versions/.*', ''
- }
- if (($subscriptionBlueprintAssignment.properties.blueprintId) -like '/providers/Microsoft.Management/managementGroups/*') {
- $blueprintScope = $subscriptionBlueprintAssignment.properties.blueprintId -replace '/providers/Microsoft.Blueprint/blueprints/.*', ''
- $blueprintName = $subscriptionBlueprintAssignment.properties.blueprintId -replace '.*/blueprints/', '' -replace '/versions/.*', ''
- }
-
- $currentTask = "Getting Blueprint definitions related to Blueprint assignments for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/$($blueprintScope)/providers/Microsoft.Blueprint/blueprints/$($blueprintName)?api-version=2018-11-01-preview"
- $method = 'GET'
- $subscriptionBlueprintDefinitionResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -listenOn 'Content' -caller 'CustomDataCollection'
-
- if ($subscriptionBlueprintDefinitionResult -eq 'BlueprintNotFound') {
- $blueprintName = 'BlueprintNotFound'
- $blueprintId = 'BlueprintNotFound'
- $blueprintAssignmentVersion = $subscriptionBlueprintAssignment.properties.blueprintId -replace '.*/'
- $blueprintDisplayName = 'BlueprintNotFound'
- $blueprintDescription = 'BlueprintNotFound'
- $blueprintScoped = $blueprintScope
- $blueprintAssignmentId = $subscriptionBlueprintAssignmentsResult.Id
- }
- else {
- $blueprintName = $subscriptionBlueprintDefinitionResult.name
- $blueprintId = $subscriptionBlueprintDefinitionResult.Id
- $blueprintAssignmentVersion = $subscriptionBlueprintAssignment.properties.blueprintId -replace '.*/'
- $blueprintDisplayName = $subscriptionBlueprintDefinitionResult.properties.displayName
- $blueprintDescription = $subscriptionBlueprintDefinitionResult.properties.description
- $blueprintScoped = $blueprintScope
- $blueprintAssignmentId = $subscriptionBlueprintAssignmentsResult.Id
- }
-
- $addRowToTableDone = $true
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $childMgDisplayName `
- -mgId $childMgId `
- -mgParentId $childMgParentId `
- -mgParentName $childMgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Subscription $scopeDisplayName `
- -SubscriptionId $scopeId `
- -SubscriptionQuotaId $subscriptionQuotaId `
- -SubscriptionState $subscriptionState `
- -SubscriptionASCSecureScore $subscriptionASCSecureScore `
- -SubscriptionTags $subscriptionTags `
- -SubscriptionTagsCount $subscriptionTagsCount `
- -BlueprintName $blueprintName `
- -BlueprintId $blueprintId `
- -BlueprintDisplayName $blueprintDisplayName `
- -BlueprintDescription $blueprintDescription `
- -BlueprintScoped $blueprintScoped `
- -BlueprintAssignmentVersion $blueprintAssignmentVersion `
- -BlueprintAssignmentId $blueprintAssignmentId
- }
- }
- }
-
- $returnObject = @{}
- if ($addRowToTableDone) {
- $returnObject.'addRowToTableDone' = @{}
- }
- return $returnObject
-}
-$funcDataCollectionBluePrintAssignmentsSub = $function:dataCollectionBluePrintAssignmentsSub.ToString()
-
-function dataCollectionPolicyExemptions {
- [CmdletBinding()]Param(
- [string]$TargetMgOrSub,
- [string]$scopeId,
- [string]$scopeDisplayName,
- $subscriptionQuotaId
- )
-
- if ($TargetMgOrSub -eq 'Sub') {
- $currentTask = "Getting Policy exemptions for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/policyExemptions?api-version=2020-07-01-preview"
- }
- if ($TargetMgOrSub -eq 'MG') {
- $currentTask = "Getting Policy exemptions for Management Group: '$($scopeDisplayName)' ('$scopeId')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($scopeId)/providers/Microsoft.Authorization/policyExemptions?api-version=2020-07-01-preview&`$filter=atScope()"
- }
- $method = 'GET'
- $requestPolicyExemptionAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $requestPolicyExemptionAPICount = ($requestPolicyExemptionAPI).Count
- if ($requestPolicyExemptionAPICount -gt 0) {
- foreach ($exemption in $requestPolicyExemptionAPI) {
- if (-not $htPolicyAssignmentExemptions.($exemption.Id)) {
- $script:htPolicyAssignmentExemptions.($exemption.Id) = @{}
- $script:htPolicyAssignmentExemptions.($exemption.Id).exemption = $exemption
- }
- }
- }
-}
-$funcDataCollectionPolicyExemptions = $function:dataCollectionPolicyExemptions.ToString()
-
-function dataCollectionPolicyDefinitions {
- [CmdletBinding()]Param(
- [string]$TargetMgOrSub,
- [string]$scopeId,
- [string]$scopeDisplayName,
- $subscriptionQuotaId
- )
-
- if ($TargetMgOrSub -eq 'Sub') {
- $currentTask = "Getting Policy definitions for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/policyDefinitions?api-version=2021-06-01&`$filter=policyType eq 'Custom'"
- }
- if ($TargetMgOrSub -eq 'MG') {
- $currentTask = "Getting Policy definitions for Management Group: '$($scopeDisplayName)' ('$scopeId')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementgroups/$($scopeId)/providers/Microsoft.Authorization/policyDefinitions?api-version=2021-06-01&`$filter=policyType eq 'Custom'"
- }
- $method = 'GET'
- $requestPolicyDefinitionAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $scopePolicyDefinitions = $requestPolicyDefinitionAPI.where( { $_.properties.policyType -eq 'custom' } )
-
- if ($TargetMgOrSub -eq 'Sub') {
- $PolicyDefinitionsScopedCount = (($scopePolicyDefinitions.where( { ($_.id) -like "/subscriptions/$($scopeId)/*" } ))).count
- }
- if ($TargetMgOrSub -eq 'MG') {
- $PolicyDefinitionsScopedCount = (($scopePolicyDefinitions.where( { ($_.id) -like "/providers/Microsoft.Management/managementGroups/$($scopeId)/*" } ))).count
- }
-
- foreach ($scopePolicyDefinition in $scopePolicyDefinitions) {
- $hlpPolicyDefinitionId = ($scopePolicyDefinition.id).ToLower()
-
- $doIt = $true
- if ($TargetMgOrSub -eq 'MG') {
- $doIt = $false
- if ($hlpPolicyDefinitionId -like "/providers/Microsoft.Management/managementGroups/$($scopeId)/*" -and $hlpPolicyDefinitionId -notlike "/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)/*") {
- $doIt = $true
- }
- if ($scopeId -eq $ManagementGroupId) {
- $doIt = $true
- }
- }
-
- if ($doIt) {
-
- if (-not $script:htCacheDefinitionsPolicy.($hlpPolicyDefinitionId)) {
- if (($scopePolicyDefinition.Properties.description).length -eq 0) {
- $policyDefinitionDescription = 'no description given'
- }
- else {
- $policyDefinitionDescription = $scopePolicyDefinition.Properties.description
- }
-
- $htTemp = @{}
- $htTemp.Id = $hlpPolicyDefinitionId
-
- if ($hlpPolicyDefinitionId -like '/providers/Microsoft.Management/managementGroups/*') {
- $htTemp.Scope = (($hlpPolicyDefinitionId).split('/'))[0..4] -join '/'
- $htTemp.ScopeMgSub = 'Mg'
- $htTemp.ScopeId = (($hlpPolicyDefinitionId).split('/'))[4]
- $htTemp.ScopeMGLevel = $htManagementGroupsMgPath.((($hlpPolicyDefinitionId).split('/'))[4]).ParentNameChainCount
- }
-
- if ($hlpPolicyDefinitionId -like '/subscriptions/*') {
- $htTemp.Scope = (($hlpPolicyDefinitionId).split('/'))[0..2] -join '/'
- $htTemp.ScopeMgSub = 'Sub'
- $htTemp.ScopeId = (($hlpPolicyDefinitionId).split('/'))[2]
- $htTemp.ScopeMGLevel = $htSubscriptionsMgPath.((($hlpPolicyDefinitionId).split('/'))[2]).level
- }
-
-
- if ($azAPICallConf['htParameters'].NoALZPolicyVersionChecker -eq $false) {
-
- $policyJsonRule = $scopePolicyDefinition.properties.policyRule | ConvertTo-Json -Depth 99
- $hash = [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policyJsonRule))
- $stringHash = [System.BitConverter]::ToString($hash)
-
- if ($alzPolicies.($scopePolicyDefinition.name) -or $alzPolicyHashes.($stringHash) -or $scopePolicyDefinition.properties.metadata.source -eq 'https://github.com/Azure/Enterprise-Scale/') {
-
- $policyHashMatch = $false
- if ($alzPolicyHashes.($stringHash)) {
- $policyHashMatch = $true
- $htTemp.ALZ = 'true'
- if ($alzPolicyHashes.($stringHash).metadataSource -eq 'https://github.com/Azure/Enterprise-Scale/' -and $alzPolicyHashes.($stringHash).metadataSource -eq $scopePolicyDefinition.properties.metadata.source -and $alzPolicyHashes.($stringHash).policyName -eq $scopePolicyDefinition.name) {
- $htTemp.ALZIdentificationLevel = 'PolicyRule Hash, Policy Name, MetaData Tag'
- }
- elseif ($alzPolicyHashes.($stringHash).policyName -eq $scopePolicyDefinition.name) {
- $htTemp.ALZIdentificationLevel = 'PolicyRule Hash, Policy Name'
- }
- else {
- $htTemp.ALZIdentificationLevel = 'PolicyRule Hash'
- }
- $htTemp.ALZPolicyName = $alzPolicyHashes.($stringHash).policyName
- $htTemp.hash = $stringHash
- if ($alzpolicies.($alzPolicyHashes.($stringHash).policyName).status -eq 'obsolete') {
- $htTemp.ALZState = 'obsolete'
- $htTemp.ALZLatestVer = ''
- }
- else {
- if ($scopePolicyDefinition.Properties.metadata.version) {
- if ($alzpolicies.($alzPolicyHashes.($stringHash).policyName).latestVersion -eq $scopePolicyDefinition.Properties.metadata.version) {
- $htTemp.ALZState = 'upToDate'
- }
- else {
- if ($alzpolicies.($alzPolicyHashes.($stringHash).policyName).latestVersion -like '*-deprecated') {
- $htTemp.ALZState = 'deprecated'
- }
- else {
- $htTemp.ALZState = 'outDated'
- }
- }
- }
- else {
- $htTemp.ALZState = 'potentiallyOutDated (no ver)'
- }
- $htTemp.ALZLatestVer = $alzpolicies.($alzPolicyHashes.($stringHash).policyName).latestVersion
- }
- }
-
- $policyNameMatch = $false
- if ($alzPolicies.($scopePolicyDefinition.name) -and -not $policyHashMatch) {
- $policyNameMatch = $true
- $htTemp.ALZ = 'true'
- if ($alzPolicies.($scopePolicyDefinition.name).metadataSource -eq 'https://github.com/Azure/Enterprise-Scale/' -and $alzPolicies.($scopePolicyDefinition.name).metadataSource -eq $scopePolicyDefinition.properties.metadata.source) {
- $htTemp.ALZIdentificationLevel = 'Policy Name, MetaData Tag'
- }
- else {
- $htTemp.ALZIdentificationLevel = 'Policy Name'
- }
-
- $htTemp.ALZPolicyName = $alzPolicies.($scopePolicyDefinition.name).policyName
- $htTemp.hash = $stringHash
- if ($alzPolicies.($scopePolicyDefinition.name).status -eq 'obsolete') {
- $htTemp.ALZState = 'obsolete'
- $htTemp.ALZLatestVer = ''
- }
- else {
- if ($scopePolicyDefinition.Properties.metadata.version) {
- if ($alzPolicies.($scopePolicyDefinition.name).latestVersion -eq $scopePolicyDefinition.Properties.metadata.version) {
- $htTemp.ALZState = 'upToDate'
- }
- else {
- if ($alzPolicies.($scopePolicyDefinition.name).latestVersion -like '*-deprecated') {
- $htTemp.ALZState = 'deprecated'
- }
- else {
- $htTemp.ALZState = 'outDated'
- }
- }
- }
- else {
- $htTemp.ALZState = 'potentiallyOutDated (no ver)'
- }
-
- $htTemp.ALZLatestVer = $alzPolicies.($scopePolicyDefinition.name).latestVersion
- }
- }
-
- if ($scopePolicyDefinition.properties.metadata.source -eq 'https://github.com/Azure/Enterprise-Scale/' -and -not $policyHashMatch -and -not $policyNameMatch) {
- $htTemp.ALZ = 'true'
- $htTemp.ALZState = 'unknown'
- $htTemp.ALZLatestVer = ''
- $htTemp.ALZIdentificationLevel = 'MetaData Tag'
- $htTemp.ALZPolicyName = ''
- $htTemp.hash = $stringHash
- }
- }
- else {
- $htTemp.ALZ = 'false'
- $htTemp.ALZState = ''
- $htTemp.ALZLatestVer = ''
- $htTemp.ALZIdentificationLevel = ''
- $htTemp.ALZPolicyName = ''
- $htTemp.hash = $stringHash
- }
- }
- else {
- $htTemp.ALZ = 'n/a'
- $htTemp.ALZState = ''
- $htTemp.ALZLatestVer = ''
- $htTemp.ALZIdentificationLevel = ''
- $htTemp.ALZPolicyName = ''
- $htTemp.hash = ''
- }
-
- $htTemp.DisplayName = $($scopePolicyDefinition.Properties.displayname)
- $htTemp.Name = $scopePolicyDefinition.Name
- $htTemp.Description = $($policyDefinitionDescription)
- $htTemp.Type = $($scopePolicyDefinition.Properties.policyType)
- $htTemp.Category = $($scopePolicyDefinition.Properties.metadata.category)
- if ($scopePolicyDefinition.Properties.metadata.version) {
- $htTemp.Version = $($scopePolicyDefinition.Properties.metadata.version)
- }
- else {
- $htTemp.Version = 'n/a'
- }
- $htTemp.PolicyDefinitionId = $hlpPolicyDefinitionId
- if ($scopePolicyDefinition.Properties.metadata.deprecated -eq $true -or $scopePolicyDefinition.Properties.displayname -like "``[Deprecated``]*") {
- $htTemp.Deprecated = $scopePolicyDefinition.Properties.metadata.deprecated
- }
- else {
- $htTemp.Deprecated = $false
- }
- if ($scopePolicyDefinition.Properties.metadata.preview -eq $true -or $scopePolicyDefinition.Properties.displayname -like "``[*Preview``]*") {
- $htTemp.Preview = $scopePolicyDefinition.Properties.metadata.preview
- }
- else {
- $htTemp.Preview = $false
- }
-
- #region effect
- $htEffectDetected = detectPolicyEffect -policyDefinition $scopePolicyDefinition
- $htTemp.effectDefaultValue = $htEffectDetected.defaultValue
- $htTemp.effectAllowedValue = $htEffectDetected.allowedValues
- $htTemp.effectFixedValue = $htEffectDetected.fixedValue
- #endregion effect
-
- $htTemp.Json = $scopePolicyDefinition
- $script:htCacheDefinitionsPolicy.($hlpPolicyDefinitionId) = $htTemp
- }
-
-
- if (-not [string]::IsNullOrWhiteSpace($scopePolicyDefinition.properties.policyRule.then.details.roleDefinitionIds)) {
- $script:htCacheDefinitionsPolicy.($hlpPolicyDefinitionId).RoleDefinitionIds = $scopePolicyDefinition.properties.policyRule.then.details.roleDefinitionIds
- foreach ($roledefinitionId in $scopePolicyDefinition.properties.policyRule.then.details.roleDefinitionIds) {
- if (-not [string]::IsNullOrEmpty($roledefinitionId)) {
- if (-not $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId)) {
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId) = @{}
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [array]$hlpPolicyDefinitionId
- }
- else {
- $usedInPolicies = $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies
- $usedInPolicies += $hlpPolicyDefinitionId
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = $usedInPolicies
- }
- }
- else {
- Write-Host "$currentTask $($hlpPolicyDefinitionId) Finding: empty roleDefinitionId in roledefinitionIds"
- }
- }
- }
- else {
- $script:htCacheDefinitionsPolicy.($hlpPolicyDefinitionId).RoleDefinitionIds = 'n/a'
- }
-
- #region namingValidation
- if (-not [string]::IsNullOrEmpty($scopePolicyDefinition.Properties.displayname)) {
- $namingValidationResult = NamingValidation -toCheck $scopePolicyDefinition.Properties.displayname
- if ($namingValidationResult.Count -gt 0) {
- if (-not $script:htNamingValidation.Policy.($hlpPolicyDefinitionId)) {
- $script:htNamingValidation.Policy.($hlpPolicyDefinitionId) = @{}
- }
- $script:htNamingValidation.Policy.($hlpPolicyDefinitionId).displayNameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.Policy.($hlpPolicyDefinitionId).displayName = $scopePolicyDefinition.Properties.displayname
- }
- }
- if (-not [string]::IsNullOrEmpty($scopePolicyDefinition.Name)) {
- $namingValidationResult = NamingValidation -toCheck $scopePolicyDefinition.Name
- if ($namingValidationResult.Count -gt 0) {
- if (-not $script:htNamingValidation.Policy.($hlpPolicyDefinitionId)) {
- $script:htNamingValidation.Policy.($hlpPolicyDefinitionId) = @{}
- }
- $script:htNamingValidation.Policy.($hlpPolicyDefinitionId).nameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.Policy.($hlpPolicyDefinitionId).name = $scopePolicyDefinition.Name
- }
- }
- #endregion namingValidation
- }
- }
-
- $returnObject = @{}
- $returnObject.'PolicyDefinitionsScopedCount' = $PolicyDefinitionsScopedCount
- return $returnObject
-}
-$funcDataCollectionPolicyDefinitions = $function:dataCollectionPolicyDefinitions.ToString()
-
-function dataCollectionPolicySetDefinitions {
- [CmdletBinding()]Param(
- [string]$TargetMgOrSub,
- [string]$scopeId,
- [string]$scopeDisplayName,
- $subscriptionQuotaId
- )
-
- if ($TargetMgOrSub -eq 'Sub') {
- $currentTask = "Getting PolicySet definitions for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/policySetDefinitions?api-version=2021-06-01&`$filter=policyType eq 'Custom'"
- }
- if ($TargetMgOrSub -eq 'MG') {
- $currentTask = "Getting PolicySet definitions for Management Group: '$($scopeDisplayName)' ('$scopeId')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementgroups/$($scopeId)/providers/Microsoft.Authorization/policySetDefinitions?api-version=2021-06-01&`$filter=policyType eq 'Custom'"
- }
- $method = 'GET'
- $requestPolicySetDefinitionAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $scopePolicySetDefinitions = $requestPolicySetDefinitionAPI.where( { $_.properties.policyType -eq 'custom' } )
- if ($TargetMgOrSub -eq 'Sub') {
- $PolicySetDefinitionsScopedCount = ($scopePolicySetDefinitions.where( { ($_.Id) -like "/subscriptions/$($scopeId)/*" } )).count
- }
- if ($TargetMgOrSub -eq 'MG') {
- $PolicySetDefinitionsScopedCount = (($scopePolicySetDefinitions.where( { ($_.Id) -like "/providers/Microsoft.Management/managementGroups/$($scopeId)/*" } ))).count
- }
-
- foreach ($scopePolicySetDefinition in $scopePolicySetDefinitions) {
- $hlpPolicySetDefinitionId = ($scopePolicySetDefinition.id).ToLower()
-
- $doIt = $true
- if ($TargetMgOrSub -eq 'MG') {
- $doIt = $false
- if ($hlpPolicySetDefinitionId -like "/providers/Microsoft.Management/managementGroups/$($scopeId)/*" -and $hlpPolicySetDefinitionId -notlike "/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)/*") {
- $doIt = $true
- }
- if ($scopeId -eq $ManagementGroupId) {
- $doIt = $true
- }
- }
-
- if ($doIt) {
- if (-not $script:htCacheDefinitionsPolicySet.($hlpPolicySetDefinitionId)) {
- if (($scopePolicySetDefinition.Properties.description).length -eq 0) {
- $policySetDefinitionDescription = 'no description given'
- }
- else {
- $policySetDefinitionDescription = $scopePolicySetDefinition.Properties.description
- }
-
- $htTemp = @{}
- $htTemp.Id = $hlpPolicySetDefinitionId
- if ($scopePolicySetDefinition.Id -like '/providers/Microsoft.Management/managementGroups/*') {
- $htTemp.Scope = (($scopePolicySetDefinition.Id).split('/'))[0..4] -join '/'
- $htTemp.ScopeMgSub = 'Mg'
- $htTemp.ScopeId = (($scopePolicySetDefinition.Id).split('/'))[4]
- $htTemp.ScopeMGLevel = $htManagementGroupsMgPath.((($scopePolicySetDefinition.Id).split('/'))[4]).ParentNameChainCount
- }
-
- if ($scopePolicySetDefinition.Id -like '/subscriptions/*') {
- $htTemp.Scope = (($scopePolicySetDefinition.Id).split('/'))[0..2] -join '/'
- $htTemp.ScopeMgSub = 'Sub'
- $htTemp.ScopeId = (($scopePolicySetDefinition.Id).split('/'))[2]
- $htTemp.ScopeMGLevel = $htSubscriptionsMgPath.((($scopePolicySetDefinition.Id).split('/'))[2]).level
- }
-
- if ($azAPICallConf['htParameters'].NoALZPolicyVersionChecker -eq $false) {
-
- $policyJsonParameters = $scopePolicySetDefinition.properties.parameters | ConvertTo-Json -Depth 99
- $policyJsonPolicyDefinitions = $scopePolicySetDefinition.properties.policyDefinitions | ConvertTo-Json -Depth 99
- $hashParameters = [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policyJsonParameters))
- $stringHashParameters = [System.BitConverter]::ToString($hashParameters)
- $hashPolicyDefinitions = [System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($policyJsonPolicyDefinitions))
- $stringHashPolicyDefinitions = [System.BitConverter]::ToString($hashPolicyDefinitions)
- $stringHash = "$($stringHashParameters)_$($stringHashPolicyDefinitions)"
-
- if ($alzPolicySets.($scopePolicySetDefinition.name) -or $allESLZPolicySetHashes.($stringHash) -or $scopePolicySetDefinition.properties.metadata.source -eq 'https://github.com/Azure/Enterprise-Scale/') {
-
- $policySetHashMatch = $false
- if ($alzPolicySetHashes.($stringHash)) {
- $policySetHashMatch = $true
- $htTemp.ALZ = 'true'
- if ($allESLZPolicySetHashes.($stringHash).metadataSource -eq 'https://github.com/Azure/Enterprise-Scale/' -and $allESLZPolicySetHashes.($stringHash).metadataSource -eq $scopePolicySetDefinition.properties.metadata.source -and $allESLZPolicySetHashes.($stringHash).policySetName -eq $scopePolicySetDefinition.name) {
- $htTemp.ALZIdentificationLevel = 'PolicySet Hash, PolicySet Name, MetaData Tag'
- }
- elseif ($allESLZPolicySetHashes.($stringHash).policySetName -eq $scopePolicySetDefinition.name) {
- $htTemp.ALZIdentificationLevel = 'PolicySet Hash, PolicySet Name'
- }
- else {
- $htTemp.ALZIdentificationLevel = 'PolicySet Hash'
- }
- $htTemp.ALZPolicySetName = $alzPolicySetHashes.($stringHash).policySetName
- if ($alzPolicySetHashes.($stringHash).status -eq 'obsolete') {
- $htTemp.ALZState = 'obsolete'
- $htTemp.ALZLatestVer = ''
- }
- else {
- if ($alzPolicySetHashes.($stringHash).latestVersion -eq $scopePolicySetDefinition.Properties.metadata.version) {
- $htTemp.ALZState = 'upToDate'
- }
- else {
- if ($alzPolicySetHashes.($stringHash).latestVersion -like '*-deprecated') {
- $htTemp.ALZState = 'deprecated'
- }
- else {
- $htTemp.ALZState = 'outDated'
- }
- }
- $htTemp.ALZLatestVer = $alzPolicySetHashes.($stringHash).latestVersion
- }
- }
-
- $policySetNameMatch = $false
- if ($alzPolicySets.($scopePolicySetDefinition.name) -and -not $policySetHashMatch) {
- $policySetNameMatch = $true
- $htTemp.ALZ = 'true'
- if ($alzPolicySets.($scopePolicySetDefinition.name).metadataSource -eq 'https://github.com/Azure/Enterprise-Scale/' -and $alzPolicySets.($scopePolicySetDefinition.name).metadataSource -eq $scopePolicySetDefinition.properties.metadata.source) {
- $htTemp.ALZIdentificationLevel = 'PolicySet Name, MetaData Tag'
- }
- else {
- $htTemp.ALZIdentificationLevel = 'PolicySet Name'
- }
- $htTemp.ALZPolicySetName = $alzPolicySets.($scopePolicySetDefinition.name).policySetName
- if ($alzPolicySets.($scopePolicySetDefinition.name).status -eq 'obsolete') {
- $htTemp.ALZState = 'obsolete'
- $htTemp.ALZLatestVer = ''
- }
- else {
- if ($alzPolicySets.($scopePolicySetDefinition.name).latestVersion -eq $scopePolicySetDefinition.Properties.metadata.version) {
- $htTemp.ALZState = 'upToDate'
- }
- else {
- if ($alzPolicySets.($scopePolicySetDefinition.name).latestVersion -like '*-deprecated') {
- $htTemp.ALZState = 'deprecated'
- }
- else {
- $htTemp.ALZState = 'outDated'
- }
- }
- $htTemp.ALZLatestVer = $alzPolicySets.($scopePolicySetDefinition.name).latestVersion
- }
- }
-
- if ($scopePolicySetDefinition.properties.metadata.source -eq 'https://github.com/Azure/Enterprise-Scale/' -and -not $policySetHashMatch -and -not $policySetNameMatch) {
- $htTemp.ALZ = 'true'
- $htTemp.ALZState = 'unknown'
- $htTemp.ALZLatestVer = ''
- $htTemp.ALZIdentificationLevel = 'MetaData Tag'
- $htTemp.ALZPolicyName = ''
- $htTemp.hash = $stringHash
- }
- }
- else {
- $htTemp.ALZ = 'false'
- $htTemp.ALZState = ''
- $htTemp.ALZLatestVer = ''
- $htTemp.ALZIdentificationLevel = ''
- $htTemp.ALZPolicySetName = ''
- }
- }
- else {
- $htTemp.ALZ = 'n/a'
- $htTemp.ALZState = ''
- $htTemp.ALZLatestVer = ''
- $htTemp.ALZIdentificationLevel = ''
- $htTemp.ALZPolicySetName = ''
- }
-
- $htTemp.DisplayName = $($scopePolicySetDefinition.Properties.displayname)
- $htTemp.Name = $scopePolicySetDefinition.Name
- $htTemp.Description = $($policySetDefinitionDescription)
- $htTemp.Type = $($scopePolicySetDefinition.Properties.policyType)
- $htTemp.Category = $($scopePolicySetDefinition.Properties.metadata.category)
- if ($scopePolicySetDefinition.Properties.metadata.version) {
- $htTemp.Version = $($scopePolicySetDefinition.Properties.metadata.version)
- }
- else {
- $htTemp.Version = 'n/a'
- }
- $htTemp.PolicyDefinitionId = $hlpPolicySetDefinitionId
- $arrayPolicySetPolicyIdsToLower = @()
- $htPolicySetPolicyRefIds = @{}
- $arrayPolicySetPolicyIdsToLower = foreach ($policySetPolicy in $scopePolicySetDefinition.properties.policydefinitions) {
- $($policySetPolicy.policyDefinitionId).ToLower()
- $htPolicySetPolicyRefIds.($policySetPolicy.policyDefinitionReferenceId) = ($policySetPolicy.policyDefinitionId)
- }
- $htTemp.PolicySetPolicyIds = $arrayPolicySetPolicyIdsToLower
- $htTemp.PolicySetPolicyRefIds = $htPolicySetPolicyRefIds
- $htTemp.Json = $scopePolicySetDefinition
- if ($scopePolicySetDefinition.Properties.metadata.deprecated -eq $true -or $scopePolicySetDefinition.Properties.displayname -like "``[Deprecated``]*") {
- $htTemp.Deprecated = $scopePolicySetDefinition.Properties.metadata.deprecated
- }
- else {
- $htTemp.Deprecated = $false
- }
- if ($scopePolicySetDefinition.Properties.metadata.preview -eq $true -or $scopePolicySetDefinition.Properties.displayname -like "``[*Preview``]*") {
- $htTemp.Preview = $scopePolicySetDefinition.Properties.metadata.preview
- }
- else {
- $htTemp.Preview = $false
- }
- ($script:htCacheDefinitionsPolicySet).($hlpPolicySetDefinitionId) = $htTemp
- }
- #namingValidation
- if (-not [string]::IsNullOrEmpty($scopePolicySetDefinition.Properties.displayname)) {
- $namingValidationResult = NamingValidation -toCheck $scopePolicySetDefinition.Properties.displayname
- if ($namingValidationResult.Count -gt 0) {
- if (-not $script:htNamingValidation.PolicySet.($scopePolicySetDefinition.Id)) {
- $script:htNamingValidation.PolicySet.($scopePolicySetDefinition.Id) = @{}
- }
- $script:htNamingValidation.PolicySet.($scopePolicySetDefinition.Id).displayNameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.PolicySet.($scopePolicySetDefinition.Id).displayName = $scopePolicySetDefinition.Properties.displayname
- }
- }
- if (-not [string]::IsNullOrEmpty($scopePolicySetDefinition.Name)) {
- $namingValidationResult = NamingValidation -toCheck $scopePolicySetDefinition.Name
- if ($namingValidationResult.Count -gt 0) {
- if (-not $script:htNamingValidation.PolicySet.($scopePolicySetDefinition.Id)) {
- $script:htNamingValidation.PolicySet.($scopePolicySetDefinition.Id) = @{}
- }
- $script:htNamingValidation.PolicySet.($scopePolicySetDefinition.Id).nameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.PolicySet.($scopePolicySetDefinition.Id).name = $scopePolicySetDefinition.Name
- }
- }
- }
- }
-
- $returnObject = @{}
- $returnObject.'PolicySetDefinitionsScopedCount' = $PolicySetDefinitionsScopedCount
- return $returnObject
-}
-$funcDataCollectionPolicySetDefinitions = $function:dataCollectionPolicySetDefinitions.ToString()
-
-function dataCollectionPolicyAssignmentsMG {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $hierarchyLevel,
- $mgParentId,
- $mgParentName,
- $mgAscSecureScoreResult,
- $PolicyDefinitionsScopedCount,
- $PolicySetDefinitionsScopedCount
- )
-
- $addRowToTableDone = $false
- $currentTask = "Getting Policy assignments for Management Group: '$($scopeDisplayName)' ('$($scopeId)')"
- if ($azAPICallConf['htParameters'].LargeTenant -eq $false -or $azAPICallConf['htParameters'].PolicyAtScopeOnly -eq $false) {
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementgroups/$($scopeId)/providers/Microsoft.Authorization/policyAssignments?`$filter=atscope()&api-version=2021-06-01"
- }
- else {
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementgroups/$($scopeId)/providers/Microsoft.Authorization/policyAssignments?`$filter=atExactScope()&api-version=2021-06-01"
- }
- $method = 'GET'
- $L0mgmtGroupPolicyAssignments = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $L0mgmtGroupPolicyAssignmentsPolicyCount = (($L0mgmtGroupPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' } ))).count
- $L0mgmtGroupPolicyAssignmentsPolicySetCount = (($L0mgmtGroupPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/' } ))).count
- $L0mgmtGroupPolicyAssignmentsPolicyAtScopeCount = (($L0mgmtGroupPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' -and $_.Id -match "/providers/Microsoft.Management/managementGroups/$($scopeId)" } ))).count
- $L0mgmtGroupPolicyAssignmentsPolicySetAtScopeCount = (($L0mgmtGroupPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/' -and $_.Id -match "/providers/Microsoft.Management/managementGroups/$($scopeId)" } ))).count
- $L0mgmtGroupPolicyAssignmentsPolicyAndPolicySetAtScopeCount = ($L0mgmtGroupPolicyAssignmentsPolicyAtScopeCount + $L0mgmtGroupPolicyAssignmentsPolicySetAtScopeCount)
-
- if (-not $htMgAtScopePolicyAssignments.($scopeId)) {
- $script:htMgAtScopePolicyAssignments.($scopeId) = @{}
- $script:htMgAtScopePolicyAssignments.($scopeId).AssignmentsCount = $L0mgmtGroupPolicyAssignmentsPolicyAndPolicySetAtScopeCount
- }
-
- foreach ($L0mgmtGroupPolicyAssignment in $L0mgmtGroupPolicyAssignments) {
-
- $doIt = $false
- if ($L0mgmtGroupPolicyAssignment.properties.scope -eq "/providers/Microsoft.Management/managementGroups/$($scopeId)" -and $L0mgmtGroupPolicyAssignment.properties.scope -ne "/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)") {
- $doIt = $true
- }
- if ($scopeId -eq $ManagementGroupId) {
- $doIt = $true
- }
-
- if ($doIt) {
- $htTemp = @{}
- $htTemp.Assignment = $L0mgmtGroupPolicyAssignment
- $htTemp.AssignmentScopeMgSubRg = 'Mg'
- $splitAssignment = (($L0mgmtGroupPolicyAssignment.Id).ToLower()).Split('/')
- $htTemp.AssignmentScopeId = [string]($splitAssignment[4])
- $script:htCacheAssignmentsPolicy.(($L0mgmtGroupPolicyAssignment.Id).ToLower()) = $htTemp
- }
-
- #region namingValidation
- if (-not [string]::IsNullOrEmpty($L0mgmtGroupPolicyAssignment.Properties.DisplayName)) {
- $namingValidationResult = NamingValidation -toCheck $L0mgmtGroupPolicyAssignment.Properties.DisplayName
- if ($namingValidationResult.Count -gt 0) {
- if (-not $script:htNamingValidation.PolicyAssignment.($L0mgmtGroupPolicyAssignment.Id)) {
- $script:htNamingValidation.PolicyAssignment.($L0mgmtGroupPolicyAssignment.Id) = @{}
- }
- $script:htNamingValidation.PolicyAssignment.($L0mgmtGroupPolicyAssignment.Id).displayNameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.PolicyAssignment.($L0mgmtGroupPolicyAssignment.Id).displayName = $L0mgmtGroupPolicyAssignment.Properties.DisplayName
- }
- }
- if (-not [string]::IsNullOrEmpty($L0mgmtGroupPolicyAssignment.Name)) {
- $namingValidationResult = NamingValidation -toCheck $L0mgmtGroupPolicyAssignment.Name
- if ($namingValidationResult.Count -gt 0) {
- if (-not $script:htNamingValidation.PolicyAssignment.($L0mgmtGroupPolicyAssignment.Id)) {
- $script:htNamingValidation.PolicyAssignment.($L0mgmtGroupPolicyAssignment.Id) = @{}
- }
- $script:htNamingValidation.PolicyAssignment.($L0mgmtGroupPolicyAssignment.Id).nameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.PolicyAssignment.($L0mgmtGroupPolicyAssignment.Id).name = $L0mgmtGroupPolicyAssignment.Name
- }
- }
- #endregion namingValidation
-
- if ($L0mgmtGroupPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' -OR $L0mgmtGroupPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/') {
-
- #policy
- if ($L0mgmtGroupPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/') {
- $policyVariant = 'Policy'
- $policyDefinitionId = ($L0mgmtGroupPolicyAssignment.properties.policydefinitionid).ToLower()
-
- $policyDefinitionSplitted = $policyDefinitionId.split('/')
- $hlpPolicyDefinitionScope = $policyDefinitionSplitted[4]
-
- if ( ($policyDefinitionId -like '/providers/microsoft.management/managementgroups/*' -and $htManagementGroupsMgPath.($scopeId).path -contains ($hlpPolicyDefinitionScope)) -or $policyDefinitionId -like '/providers/microsoft.authorization/policydefinitions/*' ) {
- $tryCounter = 0
- do {
- $tryCounter++
- if (($htCacheDefinitionsPolicy).($policyDefinitionId)) {
- $policyReturnedFromHt = $true
- $policyDefinition = ($htCacheDefinitionsPolicy).($policyDefinitionId)
-
- if ([string]::IsnullOrEmpty($policyDefinition.PolicyDefinitionId)) {
- Write-Host "check: $policyDefinitionId"
- $policyDefinition
- }
-
- if ($policyDefinition.Type -eq 'Custom') {
- $policyDefintionScope = $policyDefinition.Scope
- $policyDefintionScopeMgSub = $policyDefinition.ScopeMgSub
- $policyDefintionScopeId = $policyDefinition.ScopeId
- }
- else {
- $policyDefintionScope = 'n/a'
- $policyDefintionScopeMgSub = 'n/a'
- $policyDefintionScopeId = 'n/a'
- }
-
- $policyAvailability = ''
- $policyDisplayName = ($policyDefinition).DisplayName
- $policyDescription = ($policyDefinition).Description
- $policyDefinitionType = ($policyDefinition).Type
- $policyDefinitionIsALZ = ($policyDefinition).ALZ
- $policyCategory = ($policyDefinition).Category
- $policyDefinitionEffectDefault = ($policyDefinition).effectDefaultValue
- $policyDefinitionEffectFixed = ($policyDefinition).effectFixedValue
- }
- else {
- #test
- Write-Host " attention! $scopeDisplayName ($scopeId); policyAssignment '$($L0mgmtGroupPolicyAssignment.Id)' policyDefinition (Policy) could not be found: '$($policyDefinitionId)' -retry"
- Start-Sleep -Seconds 1
- }
- }
- until ($policyReturnedFromHt -or $tryCounter -gt 2)
- if (-not $policyReturnedFromHt) {
- Write-Host " attention! $scopeDisplayName ($scopeId); policyAssignment '$($L0mgmtGroupPolicyAssignment.Id)' policyDefinition (Policy) could not be found: '$($policyDefinitionId)'"
- Write-Host " scope: $($scopeId) Policy / Custom:$($mgPolicyDefinitions.Count) CustomAtScope:$($PolicyDefinitionsScopedCount)"
- Write-Host " built-in PolicyDefinitions: $($($htCacheDefinitionsPolicy).Values.where({$_.Type -eq 'BuiltIn'}).Count)"
- Write-Host " custom PolicyDefinitions: $($($htCacheDefinitionsPolicy).Values.where({$_.Type -eq 'Custom'}).Count)"
- Write-Host ' Listing all PolicyDefinitions:'
- foreach ($tmpPolicyDefinitionId in ($($htCacheDefinitionsPolicy).Keys | Sort-Object)) {
- Write-Host $tmpPolicyDefinitionId
- }
- Throw 'Error - Azure Governance Visualizer: check the last console output for details'
- }
- }
- #policyDefinition Scope does not exist
- else {
- if ($htManagementGroupsMgPath.Keys -contains $hlpPolicyDefinitionScope) {
- Write-Host " $scopeDisplayName ($scopeId); policyAssignment '$($L0mgmtGroupPolicyAssignment.Id)' policyDefinition (Policy) could not be found: '$($policyDefinitionId)' - the scope '$($hlpPolicyDefinitionScope)' is not contained in the '$scopeId' Management Group chain. The Policy definition scope '$hlpPolicyDefinitionScope' has MGPath: '$($htManagementGroupsMgPath.($hlpPolicyDefinitionScope).pathDelimited)'"
- }
- else {
- Write-Host " $scopeDisplayName ($scopeId); policyAssignment '$($L0mgmtGroupPolicyAssignment.Id)' policyDefinition (Policy) could not be found: '$($policyDefinitionId)' - the scope '$($hlpPolicyDefinitionScope)' could not be found"
- }
- $policyAvailability = 'na'
-
- $policyDefintionScope = "/$($policyDefinitionSplitted[1])/$($policyDefinitionSplitted[2])/$($policyDefinitionSplitted[3])/$($hlpPolicyDefinitionScope)"
- $policyDefintionScopeMgSub = 'Mg'
- $policyDefintionScopeId = $hlpPolicyDefinitionScope
-
- $policyDisplayName = 'unknown'
- $policyDescription = 'unknown'
- $policyDefinitionType = 'likely Custom'
- $policyDefinitionIsALZ = 'unknown'
- $policyCategory = 'unknown'
- $policyDefinitionEffectDefault = 'unknown'
- $policyDefinitionEffectFixed = 'unknown'
- }
-
- $policyAssignmentScope = $L0mgmtGroupPolicyAssignment.Properties.Scope
- $policyAssignmentId = ($L0mgmtGroupPolicyAssignment.Id).ToLower()
- $policyAssignmentName = $L0mgmtGroupPolicyAssignment.Name
- $policyAssignmentDisplayName = $L0mgmtGroupPolicyAssignment.Properties.DisplayName
- if (($L0mgmtGroupPolicyAssignment.Properties.Description).length -eq 0) {
- $policyAssignmentDescription = 'no description given'
- }
- else {
- $policyAssignmentDescription = $L0mgmtGroupPolicyAssignment.Properties.Description
- }
-
- if ($L0mgmtGroupPolicyAssignment.identity) {
- $policyAssignmentIdentity = $L0mgmtGroupPolicyAssignment.identity.principalId
- }
- else {
- $policyAssignmentIdentity = 'n/a'
- }
-
- $assignedBy = 'n/a'
- $createdBy = ''
- $createdOn = ''
- $updatedBy = ''
- $updatedOn = ''
- if ($L0mgmtGroupPolicyAssignment.properties.metadata) {
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.assignedBy) {
- $assignedBy = $L0mgmtGroupPolicyAssignment.properties.metadata.assignedBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.createdBy) {
- $createdBy = $L0mgmtGroupPolicyAssignment.properties.metadata.createdBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.createdOn) {
- $createdOn = $L0mgmtGroupPolicyAssignment.properties.metadata.createdOn
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.updatedBy) {
- $updatedBy = $L0mgmtGroupPolicyAssignment.properties.metadata.updatedBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.updatedOn) {
- $updatedOn = $L0mgmtGroupPolicyAssignment.properties.metadata.updatedOn
- }
- }
-
- if ($L0mgmtGroupPolicyAssignment.Properties.nonComplianceMessages.Message) {
- $nonComplianceMessage = $L0mgmtGroupPolicyAssignment.Properties.nonComplianceMessages.Message
- }
- else {
- $nonComplianceMessage = ''
- }
-
- $formatedPolicyAssignmentParameters = ''
- $hlp = $L0mgmtGroupPolicyAssignment.Properties.Parameters
- if (-not [string]::IsNullOrEmpty($hlp)) {
- $arrayPolicyAssignmentParameters = @()
- $arrayPolicyAssignmentParameters = foreach ($parameterName in $hlp.PSObject.Properties.Name | Sort-Object) {
- "$($parameterName)=$($hlp.($parameterName).Value -join "$($CsvDelimiter) ")"
- }
- $formatedPolicyAssignmentParameters = $arrayPolicyAssignmentParameters -join "$($CsvDelimiterOpposite) "
- }
-
- $addRowToTableDone = $true
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $scopeDisplayName `
- -mgId $scopeId `
- -mgParentId $mgParentId `
- -mgParentName $mgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Policy $policyDisplayName `
- -PolicyAvailability $policyAvailability `
- -PolicyDescription $policyDescription `
- -PolicyVariant $policyVariant `
- -PolicyType $policyDefinitionType `
- -PolicyIsALZ $policyDefinitionIsALZ `
- -PolicyCategory $policyCategory `
- -PolicyDefinitionIdGuid ($policyDefinitionId -replace '.*/') `
- -PolicyDefinitionId $policyDefinitionId `
- -PolicyDefintionScope $policyDefintionScope `
- -PolicyDefintionScopeMgSub $policyDefintionScopeMgSub `
- -PolicyDefintionScopeId $policyDefintionScopeId `
- -PolicyDefinitionsScopedLimit $LimitPOLICYPolicyDefinitionsScopedManagementGroup `
- -PolicyDefinitionsScopedCount $policyDefinitionsScopedCount `
- -PolicySetDefinitionsScopedLimit $LimitPOLICYPolicySetDefinitionsScopedManagementGroup `
- -PolicySetDefinitionsScopedCount $policySetDefinitionsScopedCount `
- -PolicyDefinitionEffectDefault $policyDefinitionEffectDefault `
- -PolicyDefinitionEffectFixed $policyDefinitionEffectFixed `
- -PolicyAssignmentScope $policyAssignmentScope `
- -PolicyAssignmentScopeMgSubRg 'Mg' `
- -PolicyAssignmentScopeName ($policyAssignmentScope -replace '.*/', '') `
- -PolicyAssignmentNotScopes $L0mgmtGroupPolicyAssignment.Properties.NotScopes `
- -PolicyAssignmentId $policyAssignmentId `
- -PolicyAssignmentName $policyAssignmentName `
- -PolicyAssignmentDisplayName $policyAssignmentDisplayName `
- -PolicyAssignmentDescription $policyAssignmentDescription `
- -PolicyAssignmentEnforcementMode $L0mgmtGroupPolicyAssignment.Properties.EnforcementMode `
- -PolicyAssignmentNonComplianceMessages $nonComplianceMessage `
- -PolicyAssignmentIdentity $policyAssignmentIdentity `
- -PolicyAssignmentLimit $LimitPOLICYPolicyAssignmentsManagementGroup `
- -PolicyAssignmentCount $L0mgmtGroupPolicyAssignmentsPolicyCount `
- -PolicyAssignmentAtScopeCount $L0mgmtGroupPolicyAssignmentsPolicyAtScopeCount `
- -PolicyAssignmentParameters $L0mgmtGroupPolicyAssignment.Properties.Parameters `
- -PolicyAssignmentParametersFormated $formatedPolicyAssignmentParameters `
- -PolicyAssignmentAssignedBy $assignedBy `
- -PolicyAssignmentCreatedBy $createdBy `
- -PolicyAssignmentCreatedOn $createdOn `
- -PolicyAssignmentUpdatedBy $updatedBy `
- -PolicyAssignmentUpdatedOn $updatedOn `
- -PolicySetAssignmentLimit $LimitPOLICYPolicySetAssignmentsManagementGroup `
- -PolicySetAssignmentCount $L0mgmtGroupPolicyAssignmentsPolicySetCount `
- -PolicySetAssignmentAtScopeCount $L0mgmtGroupPolicyAssignmentsPolicySetAtScopeCount `
- -PolicyAndPolicySetAssignmentAtScopeCount $L0mgmtGroupPolicyAssignmentsPolicyAndPolicySetAtScopeCount
- }
-
- #policySet
- if ($L0mgmtGroupPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/') {
- $policyVariant = 'PolicySet'
- $policySetDefinitionId = ($L0mgmtGroupPolicyAssignment.properties.policydefinitionid).ToLower()
- $policySetDefinitionSplitted = $policySetDefinitionId.split('/')
- $hlpPolicySetDefinitionScope = $policySetDefinitionSplitted[4]
-
- $tryCounter = 0
- do {
- $tryCounter++
- if (($htCacheDefinitionsPolicySet).($policySetDefinitionId)) {
- $policySetReturnedFromHt = $true
- $policySetDefinition = ($htCacheDefinitionsPolicySet).($policySetDefinitionId)
- if ($policySetDefinition.Type -eq 'Custom') {
- $policySetDefintionScope = $policySetDefinition.Scope
- $policySetDefintionScopeMgSub = $policySetDefinition.ScopeMgSub
- $policySetDefintionScopeId = $policySetDefinition.ScopeId
- }
- else {
- $policySetDefintionScope = 'n/a'
- $policySetDefintionScopeMgSub = 'n/a'
- $policySetDefintionScopeId = 'n/a'
- }
- $policySetDisplayName = $policySetDefinition.DisplayName
- $policySetDescription = $policySetDefinition.Description
- $policySetDefinitionType = $policySetDefinition.Type
- $policySetDefinitionIsALZ = $policySetDefinition.ALZ
- $policySetCategory = $policySetDefinition.Category
- }
- else {
- #test
- #Write-Host "pa '($L0mgmtGroupPolicyAssignment.Id)' scope: '$($scopeId)' - policySetDefinition not available: $policySetDefinitionId"
- Start-Sleep -Seconds 1
- }
- }
- until ($policySetReturnedFromHt -or $tryCounter -gt 2)
- if (-not $policySetReturnedFromHt) {
- Write-Host " $scopeDisplayName ($scopeId); policyAssignment '$($L0mgmtGroupPolicyAssignment.Id)' policyDefinition (PolicySet) could not be found: '$($policySetDefinitionId)'"
- $policySetDefintionScope = "/$($policySetDefinitionSplitted[1])/$($policySetDefinitionSplitted[2])/$($policySetDefinitionSplitted[3])/$($hlpPolicySetDefinitionScope)"
- $policySetDefintionScopeMgSub = 'Mg'
- $policySetDefintionScopeId = $hlpPolicySetDefinitionScope
- $policySetDisplayName = 'unknown'
- $policySetDescription = 'unknown'
- $policySetDefinitionType = 'likely Custom'
- $policySetDefinitionIsALZ = 'unknown'
- $policySetCategory = 'unknown'
- }
-
- $policyAssignmentScope = $L0mgmtGroupPolicyAssignment.Properties.Scope
- $policyAssignmentId = ($L0mgmtGroupPolicyAssignment.Id).ToLower()
- $policyAssignmentName = $L0mgmtGroupPolicyAssignment.Name
- $policyAssignmentDisplayName = $L0mgmtGroupPolicyAssignment.Properties.DisplayName
- if (($L0mgmtGroupPolicyAssignment.Properties.Description).length -eq 0) {
- $policyAssignmentDescription = 'no description given'
- }
- else {
- $policyAssignmentDescription = $L0mgmtGroupPolicyAssignment.Properties.Description
- }
-
- if ($L0mgmtGroupPolicyAssignment.identity) {
- $policyAssignmentIdentity = $L0mgmtGroupPolicyAssignment.identity.principalId
- }
- else {
- $policyAssignmentIdentity = 'n/a'
- }
-
- $assignedBy = 'n/a'
- $createdBy = ''
- $createdOn = ''
- $updatedBy = ''
- $updatedOn = ''
- if ($L0mgmtGroupPolicyAssignment.properties.metadata) {
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.assignedBy) {
- $assignedBy = $L0mgmtGroupPolicyAssignment.properties.metadata.assignedBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.createdBy) {
- $createdBy = $L0mgmtGroupPolicyAssignment.properties.metadata.createdBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.createdOn) {
- $createdOn = $L0mgmtGroupPolicyAssignment.properties.metadata.createdOn
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.updatedBy) {
- $updatedBy = $L0mgmtGroupPolicyAssignment.properties.metadata.updatedBy
- }
- if ($L0mgmtGroupPolicyAssignment.properties.metadata.updatedOn) {
- $updatedOn = $L0mgmtGroupPolicyAssignment.properties.metadata.updatedOn
- }
- }
-
- if (($L0mgmtGroupPolicyAssignment.Properties.nonComplianceMessages.where( { -not $_.policyDefinitionReferenceId } )).Message) {
- $nonComplianceMessage = ($L0mgmtGroupPolicyAssignment.Properties.nonComplianceMessages.where( { -not $_.policyDefinitionReferenceId } )).Message
- }
- else {
- $nonComplianceMessage = ''
- }
-
- $formatedPolicyAssignmentParameters = ''
- $hlp = $L0mgmtGroupPolicyAssignment.Properties.Parameters
- if (-not [string]::IsNullOrEmpty($hlp)) {
- $arrayPolicyAssignmentParameters = @()
- $arrayPolicyAssignmentParameters = foreach ($parameterName in $hlp.PSObject.Properties.Name | Sort-Object) {
- "$($parameterName)=$($hlp.($parameterName).Value -join "$($CsvDelimiter) ")"
- }
- $formatedPolicyAssignmentParameters = $arrayPolicyAssignmentParameters -join "$($CsvDelimiterOpposite) "
- }
-
- $addRowToTableDone = $true
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $scopeDisplayName `
- -mgId $scopeId `
- -mgParentId $mgParentId `
- -mgParentName $mgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Policy $policySetDisplayName `
- -PolicyDescription $policySetDescription `
- -PolicyVariant $policyVariant `
- -PolicyType $policySetDefinitionType `
- -PolicyIsALZ $policySetDefinitionIsALZ `
- -PolicyCategory $policySetCategory `
- -PolicyDefinitionIdGuid ($policySetDefinitionId -replace '.*/') `
- -PolicyDefinitionId $policySetDefinitionId `
- -PolicyDefintionScope $policySetDefintionScope `
- -PolicyDefintionScopeMgSub $policySetDefintionScopeMgSub `
- -PolicyDefintionScopeId $policySetDefintionScopeId `
- -PolicyDefinitionsScopedLimit $LimitPOLICYPolicyDefinitionsScopedManagementGroup `
- -PolicyDefinitionsScopedCount $policyDefinitionsScopedCount `
- -PolicySetDefinitionsScopedLimit $LimitPOLICYPolicySetDefinitionsScopedManagementGroup `
- -PolicySetDefinitionsScopedCount $policySetDefinitionsScopedCount `
- -PolicyAssignmentScope $policyAssignmentScope `
- -PolicyAssignmentScopeMgSubRg 'Mg' `
- -PolicyAssignmentScopeName ($policyAssignmentScope -replace '.*/', '') `
- -PolicyAssignmentNotScopes $L0mgmtGroupPolicyAssignment.Properties.NotScopes `
- -PolicyAssignmentId $policyAssignmentId `
- -PolicyAssignmentName $policyAssignmentName `
- -PolicyAssignmentDisplayName $policyAssignmentDisplayName `
- -PolicyAssignmentDescription $policyAssignmentDescription `
- -PolicyAssignmentEnforcementMode $L0mgmtGroupPolicyAssignment.Properties.EnforcementMode `
- -PolicyAssignmentNonComplianceMessages $nonComplianceMessage `
- -PolicyAssignmentIdentity $policyAssignmentIdentity `
- -PolicyAssignmentLimit $LimitPOLICYPolicyAssignmentsManagementGroup `
- -PolicyAssignmentCount $L0mgmtGroupPolicyAssignmentsPolicyCount `
- -PolicyAssignmentAtScopeCount $L0mgmtGroupPolicyAssignmentsPolicyAtScopeCount `
- -PolicyAssignmentParameters $L0mgmtGroupPolicyAssignment.Properties.Parameters `
- -PolicyAssignmentParametersFormated $formatedPolicyAssignmentParameters `
- -PolicyAssignmentAssignedBy $assignedBy `
- -PolicyAssignmentCreatedBy $createdBy `
- -PolicyAssignmentCreatedOn $createdOn `
- -PolicyAssignmentUpdatedBy $updatedBy `
- -PolicyAssignmentUpdatedOn $updatedOn `
- -PolicySetAssignmentLimit $LimitPOLICYPolicySetAssignmentsManagementGroup `
- -PolicySetAssignmentCount $L0mgmtGroupPolicyAssignmentsPolicySetCount `
- -PolicySetAssignmentAtScopeCount $L0mgmtGroupPolicyAssignmentsPolicySetAtScopeCount `
- -PolicyAndPolicySetAssignmentAtScopeCount $L0mgmtGroupPolicyAssignmentsPolicyAndPolicySetAtScopeCount
- }
- }
- }
-
- $returnObject = @{}
- if ($addRowToTableDone) {
- $returnObject.'addRowToTableDone' = @{}
- }
- return $returnObject
-}
-$funcDataCollectionPolicyAssignmentsMG = $function:dataCollectionPolicyAssignmentsMG.ToString()
-
-function dataCollectionPolicyAssignmentsSub {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $hierarchyLevel,
- $childMgDisplayName,
- $childMgId,
- $childMgParentId,
- $childMgParentName,
- $mgAscSecureScoreResult,
- $subscriptionQuotaId,
- $subscriptionState,
- $subscriptionASCSecureScore,
- $subscriptionTags,
- $subscriptionTagsCount,
- $PolicyDefinitionsScopedCount,
- $PolicySetDefinitionsScopedCount
- )
-
- $currentTask = "Getting Policy assignments for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/policyAssignments?api-version=2021-06-01"
- $method = 'GET'
-
- $addRowToTableDone = $false
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy -eq $false) {
- $L1mgmtGroupSubPolicyAssignments = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $L1mgmtGroupSubPolicyAssignmentsPolicyCount = ($L1mgmtGroupSubPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' } )).count
- $L1mgmtGroupSubPolicyAssignmentsPolicySetCount = ($L1mgmtGroupSubPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/' } )).count
- $L1mgmtGroupSubPolicyAssignmentsPolicyAtScopeCount = ($L1mgmtGroupSubPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' -and $_.Id -match "/subscriptions/$($scopeId)" } )).count
- $L1mgmtGroupSubPolicyAssignmentsPolicySetAtScopeCount = ($L1mgmtGroupSubPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/' -and $_.Id -match "/subscriptions/$($scopeId)" } )).count
- $L1mgmtGroupSubPolicyAssignmentsQuery = $L1mgmtGroupSubPolicyAssignments
- }
- else {
- $L1mgmtGroupSubPolicyAssignments = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $L1mgmtGroupSubPolicyAssignmentsPolicyCount = ($L1mgmtGroupSubPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' -and $_.Id -notmatch "/subscriptions/$($scopeId)/resourceGroups" } )).count
- $L1mgmtGroupSubPolicyAssignmentsPolicySetCount = ($L1mgmtGroupSubPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/' -and $_.Id -notmatch "/subscriptions/$($scopeId)/resourceGroups" } )).count
- $L1mgmtGroupSubPolicyAssignmentsPolicyAtScopeCount = ($L1mgmtGroupSubPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' -and $_.Id -match "/subscriptions/$($scopeId)" -and $_.Id -notmatch "/subscriptions/$($scopeId)/resourceGroups" } )).count
- $L1mgmtGroupSubPolicyAssignmentsPolicySetAtScopeCount = ($L1mgmtGroupSubPolicyAssignments.where( { $_.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/' -and $_.Id -match "/subscriptions/$($scopeId)" -and $_.Id -notmatch "/subscriptions/$($scopeId)/resourceGroups" } )).count
- foreach ($L1mgmtGroupSubPolicyAssignment in $L1mgmtGroupSubPolicyAssignments.where( { $_.Id -match "/subscriptions/$($scopeId)/resourceGroups" } )) {
- ($script:htCacheAssignmentsPolicyOnResourceGroupsAndResources).(($L1mgmtGroupSubPolicyAssignment.Id).ToLower()) = $L1mgmtGroupSubPolicyAssignment
- }
- $L1mgmtGroupSubPolicyAssignmentsQuery = $L1mgmtGroupSubPolicyAssignments.where( { $_.Id -notmatch "/subscriptions/$($scopeId)/resourceGroups" } )
- }
-
- $L1mgmtGroupSubPolicyAssignmentsPolicyAndPolicySetAtScopeCount = ($L1mgmtGroupSubPolicyAssignmentsPolicyAtScopeCount + $L1mgmtGroupSubPolicyAssignmentsPolicySetAtScopeCount)
-
- foreach ($L1mgmtGroupSubPolicyAssignment in $L1mgmtGroupSubPolicyAssignmentsQuery ) {
- if ($L1mgmtGroupSubPolicyAssignment.Id -like "/subscriptions/$($scopeId)/*") {
- $htTemp = @{}
- $htTemp.Assignment = $L1mgmtGroupSubPolicyAssignment
- $splitAssignment = (($L1mgmtGroupSubPolicyAssignment.Id).ToLower()).Split('/')
- if (($L1mgmtGroupSubPolicyAssignment.Id).ToLower() -like "/subscriptions/$($scopeId)/resourceGroups*") {
- $htTemp.AssignmentScopeMgSubRg = 'Rg'
- $htTemp.AssignmentScopeId = "$($splitAssignment[2])/$($splitAssignment[4])"
- }
- else {
- $htTemp.AssignmentScopeMgSubRg = 'Sub'
- $htTemp.AssignmentScopeId = [string]$splitAssignment[2]
- }
- $script:htCacheAssignmentsPolicy.(($L1mgmtGroupSubPolicyAssignment.Id).ToLower()) = $htTemp
- }
-
- #region namingValidation
- if (-not [string]::IsNullOrEmpty($L1mgmtGroupSubPolicyAssignment.Properties.DisplayName)) {
- $namingValidationResult = NamingValidation -toCheck $L1mgmtGroupSubPolicyAssignment.Properties.DisplayName
- if ($namingValidationResult.Count -gt 0) {
- if (-not $script:htNamingValidation.PolicyAssignment.($L1mgmtGroupSubPolicyAssignment.Id)) {
- $script:htNamingValidation.PolicyAssignment.($L1mgmtGroupSubPolicyAssignment.Id) = @{}
- }
- $script:htNamingValidation.PolicyAssignment.($L1mgmtGroupSubPolicyAssignment.Id).displayNameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.PolicyAssignment.($L1mgmtGroupSubPolicyAssignment.Id).displayName = $L1mgmtGroupSubPolicyAssignment.Properties.DisplayName
- }
- }
- if (-not [string]::IsNullOrEmpty($L1mgmtGroupSubPolicyAssignment.Name)) {
- $namingValidationResult = NamingValidation -toCheck $L1mgmtGroupSubPolicyAssignment.Name
- if ($namingValidationResult.Count -gt 0) {
- if (-not $script:htNamingValidation.PolicyAssignment.($L1mgmtGroupSubPolicyAssignment.Id)) {
- $script:htNamingValidation.PolicyAssignment.($L1mgmtGroupSubPolicyAssignment.Id) = @{}
- }
- $script:htNamingValidation.PolicyAssignment.($L1mgmtGroupSubPolicyAssignment.Id).nameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.PolicyAssignment.($L1mgmtGroupSubPolicyAssignment.Id).name = $L1mgmtGroupSubPolicyAssignment.Name
- }
- }
- #endregion namingValidation
-
- if ($L1mgmtGroupSubPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/' -OR $L1mgmtGroupSubPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/') {
-
- #policy
- if ($L1mgmtGroupSubPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policyDefinitions/') {
- $policyVariant = 'Policy'
- $policyDefinitionId = ($L1mgmtGroupSubPolicyAssignment.properties.policydefinitionid).ToLower()
-
- if (($htCacheDefinitionsPolicy).($policyDefinitionId)) {
- $policyAvailability = ''
-
- #handling some strange scenario where the synchronized hashTable responds fragments?!
- $tryCounter = 0
- do {
- $tryCounter++
- $policyAssignmentsPolicyDefinition = ($htCacheDefinitionsPolicy).($policyDefinitionId)
-
- if (($policyAssignmentsPolicyDefinition).Type -eq 'Custom' -or ($policyAssignmentsPolicyDefinition).Type -eq 'Builtin') {
- $policyReturnedFromHt = $true
-
- $policyDisplayName = ($policyAssignmentsPolicyDefinition).DisplayName
- $policyDescription = ($policyAssignmentsPolicyDefinition).Description
- $policyDefinitionType = ($policyAssignmentsPolicyDefinition).Type
- $policyDefinitionIsALZ = ($policyAssignmentsPolicyDefinition).ALZ
- $policyCategory = ($policyAssignmentsPolicyDefinition).Category
- $policyDefinitionEffectDefault = ($policyAssignmentsPolicyDefinition).effectDefaultValue
- $policyDefinitionEffectFixed = ($policyAssignmentsPolicyDefinition).effectFixedValue
-
- if (($policyAssignmentsPolicyDefinition).Type -ne $policyDefinitionType) {
- Write-Host "$scopeDisplayName ($scopeId) $policyVariant was processing: $policyDefinitionId"
- Write-Host "'$(($policyAssignmentsPolicyDefinition).Type)' ne '$policyDefinitionType'"
- Write-Host "!Please report this error: $($azAPICallConf['htParameters'].GithubRepository)" -ForegroundColor Yellow
- throw
- }
-
- if ($policyDefinitionType -eq 'Custom') {
- $policyDefintionScope = ($policyAssignmentsPolicyDefinition).Scope
- $policyDefintionScopeMgSub = ($policyAssignmentsPolicyDefinition).ScopeMgSub
- $policyDefintionScopeId = ($policyAssignmentsPolicyDefinition).ScopeId
- }
-
- if ($policyDefinitionType -eq 'Builtin') {
- $policyDefintionScope = 'n/a'
- $policyDefintionScopeMgSub = 'n/a'
- $policyDefintionScopeId = 'n/a'
- }
- }
- else {
- Write-Host " **INCONSISTENCY! processing policyId:'$policyDefinitionId'; policyAss:'$($L1mgmtGroupSubPolicyAssignment.Id)'; policyAssignmentsPolicyDefinition.Type: '$($policyAssignmentsPolicyDefinition.Type)'"
- Start-Sleep -Seconds 1
- }
- }
- until($policyReturnedFromHt -or $tryCounter -gt 5)
- if (-not $policyReturnedFromHt) {
- Write-Host "FinalHandler - $scopeDisplayName ($scopeId) $policyVariant was processing: policyId:'$policyDefinitionId'; policyAss:'$($L1mgmtGroupSubPolicyAssignment.Id)'; policyAssignmentsPolicyDefinition.Type: '$($policyAssignmentsPolicyDefinition.Type)'"
- Write-Host ($policyAssignmentsPolicyDefinition | ConvertTo-Json -Depth 99)
- Write-Host "!Please report this error: $($azAPICallConf['htParameters'].GithubRepository)" -ForegroundColor Yellow
- throw
- }
- }
- #policyDefinition not exists!
- else {
- $policyDefinitionSplitted = $policyDefinitionId.split('/')
-
- if ($policyDefinitionId -like '/providers/microsoft.management/managementgroups/*') {
- $hlpPolicyDefinitionScope = $policyDefinitionSplitted[4]
- if ($htSubscriptionsMgPath.($scopeId).path -contains $hlpPolicyDefinitionScope) {
- Write-Host " ATTENTION: $scopeDisplayName ($scopeId); policyAssignment '$($L1mgmtGroupSubPolicyAssignment.Id)' policyDefinition (Policy) could not be found: '$($policyDefinitionId)' - the scope '$($hlpPolicyDefinitionScope)' HOWEVER IS CONTAINED in the '$scopeId' Management Group chain. The Policy definition scope '$hlpPolicyDefinitionScope' has MGPath: '$($htManagementGroupsMgPath.($hlpPolicyDefinitionScope).pathDelimited)'"
- }
- else {
- if ($htManagementGroupsMgPath.($hlpPolicyDefinitionScope)) {
- Write-Host " $scopeDisplayName ($scopeId); policyAssignment '$($L1mgmtGroupSubPolicyAssignment.Id)' policyDefinition (Policy) could not be found: '$($policyDefinitionId)' - the scope '$($hlpPolicyDefinitionScope)' IS NOT CONTAINED in the '$scopeId' Management Group chain. The Policy definition scope '$hlpPolicyDefinitionScope' has MGPath: '$($htManagementGroupsMgPath.($hlpPolicyDefinitionScope).pathDelimited)'"
- }
- else {
- Write-Host " $scopeDisplayName ($scopeId); policyAssignment '$($L1mgmtGroupSubPolicyAssignment.Id)' policyDefinition (Policy) could not be found: '$($policyDefinitionId)' - the scope '$($hlpPolicyDefinitionScope)' IS NOT CONTAINED in the '$scopeId' Management Group chain. The Policy definition scope '$hlpPolicyDefinitionScope' could not be found"
- }
- }
- $policyDefintionScope = "/$($policyDefinitionSplitted[1])/$($policyDefinitionSplitted[2])/$($policyDefinitionSplitted[3])/$($hlpPolicyDefinitionScope)"
- $policyDefintionScopeMgSub = 'Mg'
- $policyDefintionScopeId = $hlpPolicyDefinitionScope
- }
- else {
- $hlpPolicyDefinitionScope = $policyDefinitionSplitted[2]
- Write-Host " $scopeDisplayName ($scopeId); policyAssignment '$($L1mgmtGroupSubPolicyAssignment.Id)' policyDefinition (Policy) could not be found: '$($policyDefinitionId)'"
- $policyDefintionScope = "/$($policyDefinitionSplitted[1])/$($hlpPolicyDefinitionScope)"
- $policyDefintionScopeMgSub = 'Sub'
- $policyDefintionScopeId = $hlpPolicyDefinitionScope
- }
- $policyAvailability = 'na'
- $policyDisplayName = 'unknown'
- $policyDescription = 'unknown'
- $policyDefinitionType = 'likely Custom'
- $policyDefinitionIsALZ = 'unknown'
- $policyCategory = 'unknown'
- $policyDefinitionEffectDefault = 'unknown'
- $policyDefinitionEffectFixed = 'unknown'
- }
-
- $PolicyAssignmentScope = $L1mgmtGroupSubPolicyAssignment.Properties.Scope
- if ($PolicyAssignmentScope -like '/providers/Microsoft.Management/managementGroups/*') {
- $PolicyAssignmentScopeMgSubRg = 'Mg'
- }
- else {
- $splitPolicyAssignmentScope = ($PolicyAssignmentScope).Split('/')
- switch (($splitPolicyAssignmentScope).Count - 1) {
- #sub
- 2 {
- $PolicyAssignmentScopeMgSubRg = 'Sub'
- }
- 4 {
- $PolicyAssignmentScopeMgSubRg = 'Rg'
- }
- Default {
- $PolicyAssignmentScopeMgSubRg = 'unknown'
- }
- }
- }
-
- $PolicyAssignmentId = ($L1mgmtGroupSubPolicyAssignment.Id).ToLower()
- $PolicyAssignmentName = $L1mgmtGroupSubPolicyAssignment.Name
- $PolicyAssignmentDisplayName = $L1mgmtGroupSubPolicyAssignment.Properties.DisplayName
- if (($L1mgmtGroupSubPolicyAssignment.Properties.Description).length -eq 0) {
- $PolicyAssignmentDescription = 'no description given'
- }
- else {
- $PolicyAssignmentDescription = $L1mgmtGroupSubPolicyAssignment.Properties.Description
- }
-
- if ($L1mgmtGroupSubPolicyAssignment.identity) {
- $PolicyAssignmentIdentity = $L1mgmtGroupSubPolicyAssignment.identity.principalId
- }
- else {
- $PolicyAssignmentIdentity = 'n/a'
- }
-
- $assignedBy = 'n/a'
- $createdBy = ''
- $createdOn = ''
- $updatedBy = ''
- $updatedOn = ''
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata) {
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata.assignedBy) {
- $assignedBy = $L1mgmtGroupSubPolicyAssignment.properties.metadata.assignedBy
- }
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata.createdBy) {
- $createdBy = $L1mgmtGroupSubPolicyAssignment.properties.metadata.createdBy
- }
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata.createdOn) {
- $createdOn = $L1mgmtGroupSubPolicyAssignment.properties.metadata.createdOn
- }
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata.updatedBy) {
- $updatedBy = $L1mgmtGroupSubPolicyAssignment.properties.metadata.updatedBy
- }
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata.updatedOn) {
- $updatedOn = $L1mgmtGroupSubPolicyAssignment.properties.metadata.updatedOn
- }
- }
-
- if ($L1mgmtGroupSubPolicyAssignment.Properties.nonComplianceMessages.Message) {
- $nonComplianceMessage = $L1mgmtGroupSubPolicyAssignment.Properties.nonComplianceMessages.Message
- }
- else {
- $nonComplianceMessage = ''
- }
-
- $formatedPolicyAssignmentParameters = ''
- $hlp = $L1mgmtGroupSubPolicyAssignment.Properties.Parameters
- if (-not [string]::IsNullOrEmpty($hlp)) {
- $arrayPolicyAssignmentParameters = @()
- $arrayPolicyAssignmentParameters = foreach ($parameterName in $hlp.PSObject.Properties.Name | Sort-Object) {
- "$($parameterName)=$($hlp.($parameterName).Value -join "$($CsvDelimiter) ")"
- }
- $formatedPolicyAssignmentParameters = $arrayPolicyAssignmentParameters -join "$($CsvDelimiterOpposite) "
- }
-
- $addRowToTableDone = $true
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $childMgDisplayName `
- -mgId $childMgId `
- -mgParentId $childMgParentId `
- -mgParentName $childMgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Subscription $scopeDisplayName `
- -SubscriptionId $scopeId `
- -SubscriptionQuotaId $subscriptionQuotaId `
- -SubscriptionState $subscriptionState `
- -SubscriptionASCSecureScore $subscriptionASCSecureScore `
- -SubscriptionTags $subscriptionTags `
- -SubscriptionTagsCount $subscriptionTagsCount `
- -Policy $policyDisplayName `
- -PolicyAvailability $policyAvailability `
- -PolicyDescription $policyDescription `
- -PolicyVariant $policyVariant `
- -PolicyType $policyDefinitionType `
- -PolicyIsALZ $policyDefinitionIsALZ `
- -PolicyCategory $policyCategory `
- -PolicyDefinitionIdGuid ($policyDefinitionId -replace '.*/') `
- -PolicyDefinitionId $policyDefinitionId `
- -PolicyDefintionScope $policyDefintionScope `
- -PolicyDefintionScopeMgSub $policyDefintionScopeMgSub `
- -PolicyDefintionScopeId $policyDefintionScopeId `
- -PolicyDefinitionsScopedLimit $LimitPOLICYPolicyDefinitionsScopedSubscription `
- -PolicyDefinitionsScopedCount $PolicyDefinitionsScopedCount `
- -PolicySetDefinitionsScopedLimit $LimitPOLICYPolicySetDefinitionsScopedSubscription `
- -PolicySetDefinitionsScopedCount $PolicySetDefinitionsScopedCount `
- -PolicyDefinitionEffectDefault $policyDefinitionEffectDefault `
- -PolicyDefinitionEffectFixed $policyDefinitionEffectFixed `
- -PolicyAssignmentScope $PolicyAssignmentScope `
- -PolicyAssignmentScopeMgSubRg $PolicyAssignmentScopeMgSubRg `
- -PolicyAssignmentScopeName ($PolicyAssignmentScope -replace '.*/', '') `
- -PolicyAssignmentNotScopes $L1mgmtGroupSubPolicyAssignment.Properties.NotScopes `
- -PolicyAssignmentId $PolicyAssignmentId `
- -PolicyAssignmentName $PolicyAssignmentName `
- -PolicyAssignmentDisplayName $PolicyAssignmentDisplayName `
- -PolicyAssignmentDescription $PolicyAssignmentDescription `
- -PolicyAssignmentEnforcementMode $L1mgmtGroupSubPolicyAssignment.Properties.EnforcementMode `
- -PolicyAssignmentNonComplianceMessages $nonComplianceMessage `
- -PolicyAssignmentIdentity $PolicyAssignmentIdentity `
- -PolicyAssignmentLimit $LimitPOLICYPolicyAssignmentsSubscription `
- -PolicyAssignmentCount $L1mgmtGroupSubPolicyAssignmentsPolicyCount `
- -PolicyAssignmentAtScopeCount $L1mgmtGroupSubPolicyAssignmentsPolicyAtScopeCount `
- -PolicyAssignmentParameters $L1mgmtGroupSubPolicyAssignment.Properties.Parameters `
- -PolicyAssignmentParametersFormated $formatedPolicyAssignmentParameters `
- -PolicyAssignmentAssignedBy $assignedBy `
- -PolicyAssignmentCreatedBy $createdBy `
- -PolicyAssignmentCreatedOn $createdOn `
- -PolicyAssignmentUpdatedBy $updatedBy `
- -PolicyAssignmentUpdatedOn $updatedOn `
- -PolicySetAssignmentLimit $LimitPOLICYPolicySetAssignmentsSubscription `
- -PolicySetAssignmentCount $L1mgmtGroupSubPolicyAssignmentsPolicySetCount `
- -PolicySetAssignmentAtScopeCount $L1mgmtGroupSubPolicyAssignmentsPolicySetAtScopeCount `
- -PolicyAndPolicySetAssignmentAtScopeCount $L1mgmtGroupSubPolicyAssignmentsPolicyAndPolicySetAtScopeCount
- }
-
- #policySet
- if ($L1mgmtGroupSubPolicyAssignment.properties.policyDefinitionId -match '/providers/Microsoft.Authorization/policySetDefinitions/') {
- $policyVariant = 'PolicySet'
- $policySetDefinitionId = ($L1mgmtGroupSubPolicyAssignment.properties.policydefinitionid).ToLower()
- $policySetDefinitionSplitted = $policySetDefinitionId.split('/')
-
- if (($htCacheDefinitionsPolicySet).($policySetDefinitionId)) {
- $policyAvailability = ''
-
- #handling some strange behavior where the synchronized hashTable responds fragments?!
- $tryCounter = 0
- do {
- $tryCounter++
- $policyAssignmentsPolicySetDefinition = ($htCacheDefinitionsPolicySet).($policySetDefinitionId)
-
- if (($policyAssignmentsPolicySetDefinition).Type -eq 'Custom' -or ($policyAssignmentsPolicySetDefinition).Type -eq 'Builtin') {
- $policySetReturnedFromHt = $true
-
- $policySetDisplayName = ($policyAssignmentsPolicySetDefinition).DisplayName
- $policySetDescription = ($policyAssignmentsPolicySetDefinition).Description
- $policySetDefinitionType = ($policyAssignmentsPolicySetDefinition).Type
- $policySetDefinitionIsALZ = ($policyAssignmentsPolicySetDefinition).ALZ
- $policySetCategory = ($policyAssignmentsPolicySetDefinition).Category
-
- if (($policyAssignmentsPolicySetDefinition).Type -ne $policySetDefinitionType) {
- Write-Host "$scopeDisplayName ($scopeId) $policyVariant was processing: $policySetDefinitionId"
- Write-Host "'$(($policyAssignmentsPolicySetDefinition).Type)' ne '$policySetDefinitionType'"
- Write-Host "!Please report this error: $($azAPICallConf['htParameters'].GithubRepository)" -ForegroundColor Yellow
- throw
- }
-
- if ($policySetDefinitionType -eq 'Custom') {
- $policySetDefintionScope = ($policyAssignmentsPolicySetDefinition).Scope
- $policySetDefintionScopeMgSub = ($policyAssignmentsPolicySetDefinition).ScopeMgSub
- $policySetDefintionScopeId = ($policyAssignmentsPolicySetDefinition).ScopeId
- }
- if ($policySetDefinitionType -eq 'Builtin') {
- $policySetDefintionScope = 'n/a'
- $policySetDefintionScopeMgSub = 'n/a'
- $policySetDefintionScopeId = 'n/a'
- }
- }
- else {
- #Write-Host "TryHandler - $scopeDisplayName ($scopeId) $policyVariant was processing: policySetId:'$policySetDefinitionId'; policyAss:'$($L1mgmtGroupSubPolicyAssignment.Id)'; type:'$(($policyAssignmentsPolicySetDefinition).Type)' - sleeping '$tryCounter' seconds"
- Start-Sleep -Seconds 1
- }
- }
- until($policySetReturnedFromHt -or $tryCounter -gt 5)
- if (-not $policySetReturnedFromHt) {
- Write-Host "FinalHandler - $scopeDisplayName ($scopeId) $policyVariant was processing: policySetId:'$policySetDefinitionId'; policyAss:'$($L1mgmtGroupSubPolicyAssignment.Id)'"
- Write-Host "!Please report this error: $($azAPICallConf['htParameters'].GithubRepository)" -ForegroundColor Yellow
- throw
- }
- }
- #policySetDefinition not exists!
- else {
- $policyAvailability = 'na'
- $policySetDisplayName = 'unknown'
- $policySetDescription = 'unknown'
- $policySetDefinitionType = 'likely Custom'
- $policySetDefinitionIsALZ = 'unknown'
- $policySetCategory = 'unknown'
-
- if ($policySetDefinitionId -like '/providers/microsoft.management/managementgroups/*') {
- $hlpPolicySetDefinitionScope = $policySetDefinitionSplitted[4]
- $policySetDefintionScope = "/$($policySetDefinitionSplitted[1])/$($policySetDefinitionSplitted[2])/$($policySetDefinitionSplitted[3])/$($hlpPolicySetDefinitionScope)"
- $policySetDefintionScopeMgSub = 'Mg'
- $policySetDefintionScopeId = $hlpPolicySetDefinitionScope
- }
- else {
- $hlpPolicySetDefinitionScope = $policySetDefinitionSplitted[2]
- $policySetDefintionScope = "/$($policySetDefinitionSplitted[1])/$($hlpPolicySetDefinitionScope)"
- $policySetDefintionScopeMgSub = 'Sub'
- $policySetDefintionScopeId = $hlpPolicySetDefinitionScope
-
- }
- Write-Host " $scopeDisplayName ($scopeId); policyAssignment '$($L1mgmtGroupSubPolicyAssignment.Id)' policyDefinition (PolicySet) could not be found: '$($policySetDefinitionId)'"
- }
-
- $PolicyAssignmentScope = $L1mgmtGroupSubPolicyAssignment.Properties.Scope
- if ($PolicyAssignmentScope -like '/providers/Microsoft.Management/managementGroups/*') {
- $PolicyAssignmentScopeMgSubRg = 'Mg'
- }
- else {
- $splitPolicyAssignmentScope = ($PolicyAssignmentScope).Split('/')
- switch (($splitPolicyAssignmentScope).Count - 1) {
- #sub
- 2 {
- $PolicyAssignmentScopeMgSubRg = 'Sub'
- }
- 4 {
- $PolicyAssignmentScopeMgSubRg = 'Rg'
- }
- Default {
- $PolicyAssignmentScopeMgSubRg = 'unknown'
- }
- }
- }
-
- $PolicyAssignmentId = ($L1mgmtGroupSubPolicyAssignment.Id).ToLower()
- $PolicyAssignmentName = $L1mgmtGroupSubPolicyAssignment.Name
- $PolicyAssignmentDisplayName = $L1mgmtGroupSubPolicyAssignment.Properties.DisplayName
- if (($L1mgmtGroupSubPolicyAssignment.Properties.Description).length -eq 0) {
- $PolicyAssignmentDescription = 'no description given'
- }
- else {
- $PolicyAssignmentDescription = $L1mgmtGroupSubPolicyAssignment.Properties.Description
- }
-
- if ($L1mgmtGroupSubPolicyAssignment.identity) {
- $PolicyAssignmentIdentity = $L1mgmtGroupSubPolicyAssignment.identity.principalId
- }
- else {
- $PolicyAssignmentIdentity = 'n/a'
- }
-
- $assignedBy = 'n/a'
- $createdBy = ''
- $createdOn = ''
- $updatedBy = ''
- $updatedOn = ''
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata) {
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata.assignedBy) {
- $assignedBy = $L1mgmtGroupSubPolicyAssignment.properties.metadata.assignedBy
- }
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata.createdBy) {
- $createdBy = $L1mgmtGroupSubPolicyAssignment.properties.metadata.createdBy
- }
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata.createdOn) {
- $createdOn = $L1mgmtGroupSubPolicyAssignment.properties.metadata.createdOn
- }
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata.updatedBy) {
- $updatedBy = $L1mgmtGroupSubPolicyAssignment.properties.metadata.updatedBy
- }
- if ($L1mgmtGroupSubPolicyAssignment.properties.metadata.updatedOn) {
- $updatedOn = $L1mgmtGroupSubPolicyAssignment.properties.metadata.updatedOn
- }
- }
-
- if (($L1mgmtGroupSubPolicyAssignment.Properties.nonComplianceMessages.where( { -not $_.policyDefinitionReferenceId })).Message) {
- $nonComplianceMessage = ($L1mgmtGroupSubPolicyAssignment.Properties.nonComplianceMessages.where( { -not $_.policyDefinitionReferenceId })).Message
- }
- else {
- $nonComplianceMessage = ''
- }
-
- $formatedPolicyAssignmentParameters = ''
- $hlp = $L1mgmtGroupSubPolicyAssignment.Properties.Parameters
- if (-not [string]::IsNullOrEmpty($hlp)) {
- $arrayPolicyAssignmentParameters = @()
- $arrayPolicyAssignmentParameters = foreach ($parameterName in $hlp.PSObject.Properties.Name | Sort-Object) {
- "$($parameterName)=$($hlp.($parameterName).Value -join "$($CsvDelimiter) ")"
- }
- $formatedPolicyAssignmentParameters = $arrayPolicyAssignmentParameters -join "$($CsvDelimiterOpposite) "
- }
-
- $addRowToTableDone = $true
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $childMgDisplayName `
- -mgId $childMgId `
- -mgParentId $childMgParentId `
- -mgParentName $childMgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Subscription $scopeDisplayName `
- -SubscriptionId $scopeId `
- -SubscriptionQuotaId $subscriptionQuotaId `
- -SubscriptionState $subscriptionState `
- -SubscriptionASCSecureScore $subscriptionASCSecureScore `
- -SubscriptionTags $subscriptionTags `
- -SubscriptionTagsCount $subscriptionTagsCount `
- -Policy $policySetDisplayName `
- -PolicyAvailability $policyAvailability `
- -PolicyDescription $policySetDescription `
- -PolicyVariant $policyVariant `
- -PolicyType $policySetDefinitionType `
- -PolicyIsALZ $policySetDefinitionIsALZ `
- -PolicyCategory $policySetCategory `
- -PolicyDefinitionIdGuid (($policySetDefinitionId) -replace '.*/') `
- -PolicyDefinitionId $policySetDefinitionId `
- -PolicyDefintionScope $policySetDefintionScope `
- -PolicyDefintionScopeMgSub $policySetDefintionScopeMgSub `
- -PolicyDefintionScopeId $policySetDefintionScopeId `
- -PolicyDefinitionsScopedLimit $LimitPOLICYPolicyDefinitionsScopedSubscription `
- -PolicyDefinitionsScopedCount $PolicyDefinitionsScopedCount `
- -PolicySetDefinitionsScopedLimit $LimitPOLICYPolicySetDefinitionsScopedSubscription `
- -PolicySetDefinitionsScopedCount $PolicySetDefinitionsScopedCount `
- -PolicyAssignmentScope $PolicyAssignmentScope `
- -PolicyAssignmentScopeMgSubRg $PolicyAssignmentScopeMgSubRg `
- -PolicyAssignmentScopeName ($PolicyAssignmentScope -replace '.*/', '') `
- -PolicyAssignmentNotScopes $L1mgmtGroupSubPolicyAssignment.Properties.NotScopes `
- -PolicyAssignmentId $PolicyAssignmentId `
- -PolicyAssignmentName $PolicyAssignmentName `
- -PolicyAssignmentDisplayName $PolicyAssignmentDisplayName `
- -PolicyAssignmentDescription $PolicyAssignmentDescription `
- -PolicyAssignmentEnforcementMode $L1mgmtGroupSubPolicyAssignment.Properties.EnforcementMode `
- -PolicyAssignmentNonComplianceMessages $nonComplianceMessage `
- -PolicyAssignmentIdentity $PolicyAssignmentIdentity `
- -PolicyAssignmentLimit $LimitPOLICYPolicyAssignmentsSubscription `
- -PolicyAssignmentCount $L1mgmtGroupSubPolicyAssignmentsPolicyCount `
- -PolicyAssignmentAtScopeCount $L1mgmtGroupSubPolicyAssignmentsPolicyAtScopeCount `
- -PolicyAssignmentParameters $L1mgmtGroupSubPolicyAssignment.Properties.Parameters `
- -PolicyAssignmentParametersFormated $formatedPolicyAssignmentParameters `
- -PolicyAssignmentAssignedBy $assignedBy `
- -PolicyAssignmentCreatedBy $createdBy `
- -PolicyAssignmentCreatedOn $createdOn `
- -PolicyAssignmentUpdatedBy $updatedBy `
- -PolicyAssignmentUpdatedOn $updatedOn `
- -PolicySetAssignmentLimit $LimitPOLICYPolicySetAssignmentsSubscription `
- -PolicySetAssignmentCount $L1mgmtGroupSubPolicyAssignmentsPolicySetCount `
- -PolicySetAssignmentAtScopeCount $L1mgmtGroupSubPolicyAssignmentsPolicySetAtScopeCount `
- -PolicyAndPolicySetAssignmentAtScopeCount $L1mgmtGroupSubPolicyAssignmentsPolicyAndPolicySetAtScopeCount
- }
- }
- }
-
- $returnObject = @{}
- if ($addRowToTableDone) {
- $returnObject.'addRowToTableDone' = @{}
- }
- return $returnObject
-}
-$funcDataCollectionPolicyAssignmentsSub = $function:dataCollectionPolicyAssignmentsSub.ToString()
-
-function dataCollectionRoleDefinitions {
- [CmdletBinding()]Param(
- [string]$TargetMgOrSub,
- [string]$scopeId,
- [string]$scopeDisplayName,
- $subscriptionQuotaId
- )
-
- if ($TargetMgOrSub -eq 'Sub') {
- $currentTask = "Getting Custom Role definitions for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/roleDefinitions?api-version=2018-07-01&`$filter=type eq 'CustomRole'"
- }
- if ($TargetMgOrSub -eq 'MG') {
- $currentTask = "Getting Custom Role definitions for Management Group: '$($scopeDisplayName)' ('$scopeId')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($scopeId)/providers/Microsoft.Authorization/roleDefinitions?api-version=2018-07-01&`$filter=type eq 'CustomRole'"
- }
- $method = 'GET'
- $scopeCustomRoleDefinitions = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- foreach ($scopeCustomRoleDefinition in $scopeCustomRoleDefinitions) {
- if (-not $($htCacheDefinitionsRole).($scopeCustomRoleDefinition.name)) {
-
- if (
- (
- $scopeCustomRoleDefinition.properties.permissions.Actions -contains 'Microsoft.Authorization/roleassignments/write' -or
- $scopeCustomRoleDefinition.properties.permissions.Actions -contains 'Microsoft.Authorization/roleassignments/*' -or
- $scopeCustomRoleDefinition.properties.permissions.Actions -contains 'Microsoft.Authorization/*/write' -or
- $scopeCustomRoleDefinition.properties.permissions.Actions -contains 'Microsoft.Authorization/*' -or
- $scopeCustomRoleDefinition.properties.permissions.Actions -contains '*/write' -or
- $scopeCustomRoleDefinition.properties.permissions.Actions -contains '*'
- ) -and (
- $scopeCustomRoleDefinition.properties.permissions.NotActions -notcontains 'Microsoft.Authorization/roleassignments/write' -and
- $scopeCustomRoleDefinition.properties.permissions.NotActions -notcontains 'Microsoft.Authorization/roleassignments/*' -and
- $scopeCustomRoleDefinition.properties.permissions.NotActions -notcontains 'Microsoft.Authorization/*/write' -and
- $scopeCustomRoleDefinition.properties.permissions.NotActions -notcontains 'Microsoft.Authorization/*' -and
- $scopeCustomRoleDefinition.properties.permissions.NotActions -notcontains '*/write' -and
- $scopeCustomRoleDefinition.properties.permissions.NotActions -notcontains '*'
- )
- ) {
- $roleCapable4RoleAssignmentsWrite = $true
- }
- else {
- $roleCapable4RoleAssignmentsWrite = $false
- }
-
- $htTemp = @{}
- $htTemp.Id = $($scopeCustomRoleDefinition.name)
- $htTemp.Name = $($scopeCustomRoleDefinition.properties.roleName)
- $htTemp.IsCustom = $true
- $htTemp.AssignableScopes = $($scopeCustomRoleDefinition.properties.AssignableScopes)
- $htTemp.Actions = $($scopeCustomRoleDefinition.properties.permissions.Actions)
- $htTemp.NotActions = $($scopeCustomRoleDefinition.properties.permissions.NotActions)
- $htTemp.DataActions = $($scopeCustomRoleDefinition.properties.permissions.DataActions)
- $htTemp.NotDataActions = $($scopeCustomRoleDefinition.properties.permissions.NotDataActions)
- $htTemp.Json = $scopeCustomRoleDefinition
- $htTemp.RoleCanDoRoleAssignments = $roleCapable4RoleAssignmentsWrite
- ($script:htCacheDefinitionsRole).($scopeCustomRoleDefinition.name) = $htTemp
-
- #namingValidation
- if (-not [string]::IsNullOrEmpty($scopeCustomRoleDefinition.properties.roleName)) {
- $namingValidationResult = NamingValidation -toCheck $scopeCustomRoleDefinition.properties.roleName
- if ($namingValidationResult.Count -gt 0) {
- $script:htNamingValidation.Role.($scopeCustomRoleDefinition.name) = @{}
- $script:htNamingValidation.Role.($scopeCustomRoleDefinition.name).roleNameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.Role.($scopeCustomRoleDefinition.name).roleName = $scopeCustomRoleDefinition.properties.roleName
- }
- }
- }
- }
-}
-$funcDataCollectionRoleDefinitions = $function:dataCollectionRoleDefinitions.ToString()
-
-function dataCollectionRoleAssignmentsMG {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $hierarchyLevel,
- $mgParentId,
- $mgParentName,
- $mgAscSecureScoreResult
- )
-
- $addRowToTableDone = $false
- #PIM MGRoleAssignmentScheduleInstances
- if ($htDoARMRoleAssignmentScheduleInstances.Do -eq $true) {
- $currentTask = "Getting ARM RoleAssignment ScheduleInstances for Management Group: '$($scopeDisplayName)' ('$($scopeId)')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($scopeId)/providers/Microsoft.Authorization/roleAssignmentScheduleInstances?api-version=2020-10-01"
- $method = 'GET'
- $roleAssignmentScheduleInstancesFromAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- if ($roleAssignmentScheduleInstancesFromAPI -eq 'RoleAssignmentScheduleInstancesError' -or $roleAssignmentScheduleInstancesFromAPI -eq 'AadPremiumLicenseRequired') {
- if ($roleAssignmentScheduleInstancesFromAPI -eq 'AadPremiumLicenseRequired') {
- Write-Host " -> Setting 'htDoARMRoleAssignmentScheduleInstances.Do' to false (AadPremiumLicenseRequired)"
- $script:htDoARMRoleAssignmentScheduleInstances.Do = $false
- }
- }
- else {
- $roleAssignmentScheduleInstances = ($roleAssignmentScheduleInstancesFromAPI.where( { ($_.properties.roleAssignmentScheduleId -replace '.*/') -ne ($_.properties.originRoleAssignmentId -replace '.*/') }))
- $roleAssignmentScheduleInstancesCount = $roleAssignmentScheduleInstances.Count
- if ($roleAssignmentScheduleInstancesCount -gt 0) {
- #$htRoleAssignmentsPIM = @{}
- foreach ($roleAssignmentScheduleInstance in $roleAssignmentScheduleInstances) {
- $script:htRoleAssignmentsPIM.($roleAssignmentScheduleInstance.properties.originRoleAssignmentId.tolower()) = $roleAssignmentScheduleInstance.properties
- }
- }
- }
- }
-
- #RoleAssignment API MG
- $currentTask = "Getting Role assignments API for Management Group: '$($scopeDisplayName)' ('$($scopeId)')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($scopeId)/providers/Microsoft.Authorization/roleAssignments?api-version=2015-07-01"
- $method = 'GET'
- $roleAssignmentsFromAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- if ($roleAssignmentsFromAPI.Count -gt 0) {
- $principalsToResolve = @()
- $principalsToResolve = foreach ($ra in $roleAssignmentsFromAPI.properties | Sort-Object -Property principalId -Unique) {
- if (-not $htPrincipals.($ra.principalId)) {
- $ra.principalId
- }
- }
-
- if ($principalsToResolve.Count -gt 0) {
- ResolveObjectIds -objectIds $principalsToResolve
- }
- }
-
- $L0mgmtGroupRoleAssignments = $roleAssignmentsFromAPI
-
- $L0mgmtGroupRoleAssignmentsLimitUtilization = (($L0mgmtGroupRoleAssignments.properties.where( { $_.scope -eq "/providers/Microsoft.Management/managementGroups/$($scopeId)" } ))).count
- if (-not $htMgAtScopeRoleAssignments.($scopeId)) {
- $script:htMgAtScopeRoleAssignments.($scopeId) = @{}
- $script:htMgAtScopeRoleAssignments.($scopeId).AssignmentsCount = $L0mgmtGroupRoleAssignmentsLimitUtilization
- }
-
- if ($azAPICallConf['htParameters'].LargeTenant -eq $true -or $azAPICallConf['htParameters'].RBACAtScopeOnly -eq $true) {
- $L0mgmtGroupRoleAssignments = $L0mgmtGroupRoleAssignments.where( { $_.properties.scope -eq "/providers/Microsoft.Management/managementGroups/$($scopeId)" } )
- }
- else {
- #tenantLevelRoleAssignments
- if (-not $htMgAtScopeRoleAssignments.'tenantLevelRoleAssignments') {
- $tenantLevelRoleAssignmentsCount = (($L0mgmtGroupRoleAssignments.where( { $_.id -like '/providers/Microsoft.Authorization/roleAssignments/*' } ))).count
- $script:htMgAtScopeRoleAssignments.'tenantLevelRoleAssignments' = @{}
- $script:htMgAtScopeRoleAssignments.'tenantLevelRoleAssignments'.AssignmentsCount = $tenantLevelRoleAssignmentsCount
- }
- }
- foreach ($L0mgmtGroupRoleAssignment in $L0mgmtGroupRoleAssignments) {
- $roleAssignmentId = ($L0mgmtGroupRoleAssignment.id).ToLower()
-
- if ($htRoleAssignmentsPIM.($roleAssignmentId)) {
- $hlperPim = $htRoleAssignmentsPIM.($roleAssignmentId)
- $pim = 'true'
- $pimAssignmentType = $hlperPim.assignmentType
- $pimSlotStart = $($hlperPim.startDateTime)
- if ($hlperPim.endDateTime) {
- $pimSlotEnd = $($hlperPim.endDateTime)
- }
- else {
- $pimSlotEnd = 'eternity'
- }
- }
- else {
- $pim = 'false'
- $pimAssignmentType = ''
- $pimSlotStart = ''
- $pimSlotEnd = ''
- }
-
- if (-not $htRoleAssignmentsFromAPIInheritancePrevention.($roleAssignmentId -replace '.*/')) {
- $script:htRoleAssignmentsFromAPIInheritancePrevention.($roleAssignmentId -replace '.*/') = @{}
- $script:htRoleAssignmentsFromAPIInheritancePrevention.($roleAssignmentId -replace '.*/').assignment = $L0mgmtGroupRoleAssignment
- }
-
- $roleDefinitionId = $L0mgmtGroupRoleAssignment.properties.roleDefinitionId
- $roleDefinitionIdGuid = $roleDefinitionId -replace '.*/'
-
- if (-not ($htCacheDefinitionsRole).($roleDefinitionIdGuid)) {
- $roleAssignmentsRoleDefinition = ''
- $roleDefinitionName = "'This roleDefinition likely was deleted although a roleAssignment existed'"
- }
- else {
- $roleAssignmentsRoleDefinition = ($htCacheDefinitionsRole).($roleDefinitionIdGuid)
- $roleDefinitionName = $roleAssignmentsRoleDefinition.Name
- }
-
- $doIt = $false
- if ($L0mgmtGroupRoleAssignment.properties.scope -eq "/providers/Microsoft.Management/managementGroups/$($scopeId)" -and $L0mgmtGroupRoleAssignment.properties.scope -ne "/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)") {
- $doIt = $true
- }
- if ($scopeId -eq $ManagementGroupId) {
- $doIt = $true
- }
-
- if ($doIt) {
- #assignment
- $splitAssignment = ($roleAssignmentId).Split('/')
- $arrayRoleAssignment = [System.Collections.ArrayList]@()
- $null = $arrayRoleAssignment.Add([PSCustomObject]@{
- RoleAssignmentId = $roleAssignmentId
- Scope = $L0mgmtGroupRoleAssignment.properties.scope
- DisplayName = $htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).displayName
- SignInName = $htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).signInName
- RoleDefinitionName = $roleDefinitionName
- RoleDefinitionId = $L0mgmtGroupRoleAssignment.properties.roleDefinitionId -replace '.*/'
- ObjectId = $L0mgmtGroupRoleAssignment.properties.principalId
- ObjectType = $htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).type
- PIM = $pim
- })
-
- $htTemp = @{}
- $htTemp.Assignment = $arrayRoleAssignment
-
- if ($roleAssignmentId -like '/providers/Microsoft.Authorization/roleAssignments/*') {
- $htTemp.AssignmentScopeTenMgSubRgRes = 'Tenant'
- $htTemp.AssignmentScopeId = 'Tenant'
- }
- else {
- $htTemp.AssignmentScopeTenMgSubRgRes = 'Mg'
- $htTemp.AssignmentScopeId = [string]$splitAssignment[4]
- }
- ($script:htCacheAssignmentsRole).($roleAssignmentId) = $htTemp
- }
-
- if (($htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).displayName).length -eq 0) {
- $roleAssignmentIdentityDisplayname = 'n/a'
- }
- else {
- if ($htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).type -eq 'User') {
- if ($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData -eq $false) {
- $roleAssignmentIdentityDisplayname = $htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).displayName
- }
- else {
- $roleAssignmentIdentityDisplayname = 'scrubbed'
- }
- }
- else {
- $roleAssignmentIdentityDisplayname = $htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).displayName
- }
- }
- if (-not $htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).signInName) {
- $roleAssignmentIdentitySignInName = 'n/a'
- }
- else {
- if ($htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).type -eq 'User') {
- if ($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData -eq $false) {
- $roleAssignmentIdentitySignInName = $htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).signInName
- }
- else {
- $roleAssignmentIdentitySignInName = 'scrubbed'
- }
- }
- else {
- $roleAssignmentIdentitySignInName = $htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).signInName
- }
- }
- $roleAssignmentIdentityObjectId = $L0mgmtGroupRoleAssignment.properties.principalId
- $roleAssignmentIdentityObjectType = $htPrincipals.($L0mgmtGroupRoleAssignment.properties.principalId).type
- $roleAssignmentScope = $L0mgmtGroupRoleAssignment.properties.scope
- $roleAssignmentScopeName = $roleAssignmentScope -replace '.*/'
- $roleAssignmentScopeType = 'MG'
-
- $roleSecurityCustomRoleOwner = 0
- if ($roleAssignmentsRoleDefinition.Actions -eq '*' -and (($roleAssignmentsRoleDefinition.NotActions)).length -eq 0 -and $roleAssignmentsRoleDefinition.IsCustom -eq $True) {
- $roleSecurityCustomRoleOwner = 1
- }
- $roleSecurityOwnerAssignmentSP = 0
- if (($roleAssignmentsRoleDefinition.Id -eq '8e3af657-a8ff-443c-a75c-2fe8c4bcb635' -and $roleAssignmentIdentityObjectType -eq 'ServicePrincipal') -or ($roleAssignmentsRoleDefinition.Actions -eq '*' -and (($roleAssignmentsRoleDefinition.NotActions)).length -eq 0 -and $roleAssignmentsRoleDefinition.IsCustom -eq $True -and $roleAssignmentIdentityObjectType -eq 'ServicePrincipal')) {
- $roleSecurityOwnerAssignmentSP = 1
- }
-
- $createdBy = ''
- $createdOn = ''
- $createdOnUnformatted = $null
- $updatedBy = ''
- $updatedOn = ''
-
- if ($L0mgmtGroupRoleAssignment.properties.createdBy) {
- $createdBy = $L0mgmtGroupRoleAssignment.properties.createdBy
- }
- if ($L0mgmtGroupRoleAssignment.properties.createdOn) {
- $createdOn = $L0mgmtGroupRoleAssignment.properties.createdOn
- }
- if ($L0mgmtGroupRoleAssignment.properties.updatedBy) {
- $updatedBy = $L0mgmtGroupRoleAssignment.properties.updatedBy
- }
- if ($L0mgmtGroupRoleAssignment.properties.updatedOn) {
- $updatedOn = $L0mgmtGroupRoleAssignment.properties.updatedOn
- }
- $createdOnUnformatted = $L0mgmtGroupRoleAssignment.properties.createdOn
-
- $addRowToTableDone = $true
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $scopeDisplayName `
- -mgId $scopeId `
- -mgParentId $mgParentId `
- -mgParentName $mgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -RoleDefinitionId $roleDefinitionIdGuid `
- -RoleDefinitionName $roleDefinitionName `
- -RoleIsCustom $roleAssignmentsRoleDefinition.IsCustom `
- -RoleAssignableScopes ($roleAssignmentsRoleDefinition.AssignableScopes -join "$CsvDelimiterOpposite ") `
- -RoleActions ($roleAssignmentsRoleDefinition.Actions -join "$CsvDelimiterOpposite ") `
- -RoleNotActions ($roleAssignmentsRoleDefinition.NotActions -join "$CsvDelimiterOpposite ") `
- -RoleDataActions ($roleAssignmentsRoleDefinition.DataActions -join "$CsvDelimiterOpposite ") `
- -RoleNotDataActions ($roleAssignmentsRoleDefinition.NotDataActions -join "$CsvDelimiterOpposite ") `
- -RoleCanDoRoleAssignments $roleAssignmentsRoleDefinition.RoleCanDoRoleAssignments `
- -RoleAssignmentIdentityDisplayname $roleAssignmentIdentityDisplayname `
- -RoleAssignmentIdentitySignInName $roleAssignmentIdentitySignInName `
- -RoleAssignmentIdentityObjectId $roleAssignmentIdentityObjectId `
- -RoleAssignmentIdentityObjectType $roleAssignmentIdentityObjectType `
- -RoleAssignmentId $roleAssignmentId `
- -RoleAssignmentScope $roleAssignmentScope `
- -RoleAssignmentScopeName $roleAssignmentScopeName `
- -RoleAssignmentScopeType $roleAssignmentScopeType `
- -RoleAssignmentCreatedBy $createdBy `
- -RoleAssignmentCreatedOn $createdOn `
- -RoleAssignmentCreatedOnUnformatted $createdOnUnformatted `
- -RoleAssignmentUpdatedBy $updatedBy `
- -RoleAssignmentUpdatedOn $updatedOn `
- -RoleAssignmentsLimit $LimitRBACRoleAssignmentsManagementGroup `
- -RoleAssignmentsCount $L0mgmtGroupRoleAssignmentsLimitUtilization `
- -RoleSecurityCustomRoleOwner $roleSecurityCustomRoleOwner `
- -RoleSecurityOwnerAssignmentSP $roleSecurityOwnerAssignmentSP `
- -RoleAssignmentPIM $pim `
- -RoleAssignmentPIMAssignmentType $pimAssignmentType `
- -RoleAssignmentPIMSlotStart $pimSlotStart `
- -RoleAssignmentPIMSlotEnd $pimSlotEnd
- }
-
- $returnObject = @{}
- if ($addRowToTableDone) {
- $returnObject.'addRowToTableDone' = @{}
- }
- return $returnObject
-}
-$funcDataCollectionRoleAssignmentsMG = $function:dataCollectionRoleAssignmentsMG.ToString()
-
-function dataCollectionRoleAssignmentsSub {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- $hierarchyLevel,
- $childMgDisplayName,
- $childMgId,
- $childMgParentId,
- $childMgParentName,
- $mgAscSecureScoreResult,
- $subscriptionQuotaId,
- $subscriptionState,
- $subscriptionASCSecureScore,
- $subscriptionTags,
- $subscriptionTagsCount
- )
-
- $addRowToTableDone = $false
- #Usage
- $currentTask = "Getting Role assignments usage metrics for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/roleAssignmentsUsageMetrics?api-version=2019-08-01-preview"
- $method = 'GET'
- $roleAssignmentsUsage = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -listenOn 'Content' -caller 'CustomDataCollection'
-
- $script:htSubscriptionsRoleAssignmentLimit.($scopeId) = $roleAssignmentsUsage.roleAssignmentsLimit
-
- #PIM SubscriptionRoleAssignmentScheduleInstances
- if ($htDoARMRoleAssignmentScheduleInstances.Do -eq $true) {
- $currentTask = "Getting ARM RoleAssignment ScheduleInstances for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/roleAssignmentScheduleInstances?api-version=2020-10-01"
- $method = 'GET'
- $roleAssignmentScheduleInstancesFromAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- if ($roleAssignmentScheduleInstancesFromAPI -eq 'RoleAssignmentScheduleInstancesError' -or $roleAssignmentScheduleInstancesFromAPI -eq 'AadPremiumLicenseRequired') {
- # this should not be required at sub level as the error would already have occured at mg level
- # if ($roleAssignmentScheduleInstancesFromAPI -eq 'AadPremiumLicenseRequired') {
- # Write-Host " Setting 'htDoARMRoleAssignmentScheduleInstances.Do' to false (AadPremiumLicenseRequired)"
- # $script:htDoARMRoleAssignmentScheduleInstances.Do = $false
- # }
- }
- else {
- $roleAssignmentScheduleInstances = ($roleAssignmentScheduleInstancesFromAPI.where( { ($_.properties.roleAssignmentScheduleId -replace '.*/') -ne ($_.properties.originRoleAssignmentId -replace '.*/') }))
- $roleAssignmentScheduleInstancesCount = $roleAssignmentScheduleInstances.Count
- if ($roleAssignmentScheduleInstancesCount -gt 0) {
- #$htRoleAssignmentsPIM = @{}
- foreach ($roleAssignmentScheduleInstance in $roleAssignmentScheduleInstances) {
- $script:htRoleAssignmentsPIM.($roleAssignmentScheduleInstance.properties.originRoleAssignmentId.tolower()) = $roleAssignmentScheduleInstance.properties
- }
- }
- }
- }
-
- #RoleAssignment API Sub
- $currentTask = "Getting Role assignments API for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/roleAssignments?api-version=2015-07-01"
- $method = 'GET'
- $roleAssignmentsFromAPI = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
-
- $baseRoleAssignments = [System.Collections.ArrayList]@()
- if ($roleAssignmentsFromAPI.Count -gt 0) {
- foreach ($roleAssignmentFromAPI in $roleAssignmentsFromAPI) {
-
- if ($roleAssignmentFromAPI.id -match "/subscriptions/$($scopeId)/") {
- if (-not $htRoleAssignmentsFromAPIInheritancePrevention.($roleAssignmentFromAPI.id -replace '.*/')) {
- $null = $baseRoleAssignments.Add($roleAssignmentFromAPI)
- }
- else {
- $null = $baseRoleAssignments.Add($htRoleAssignmentsFromAPIInheritancePrevention.($roleAssignmentFromAPI.id -replace '.*/').assignment)
- }
- }
- else {
- $null = $baseRoleAssignments.Add($roleAssignmentFromAPI)
- }
- }
- }
-
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC -eq $true) {
- $relevantRAs = $baseRoleAssignments.where( { $_.id -notmatch "/subscriptions/$($scopeId)/resourcegroups/" } )
- }
- else {
- $relevantRAs = $baseRoleAssignments
- }
- if ($relevantRAs.Count -gt 0) {
- $principalsToResolve = @()
- $principalsToResolve = foreach ($ra in $relevantRAs.properties | Sort-Object -Property principalId -Unique) {
- if (-not $htPrincipals.($ra.principalId)) {
- $ra.principalId
- }
- }
-
- if ($principalsToResolve.Count -gt 0) {
- ResolveObjectIds -objectIds $principalsToResolve
- }
- }
-
-
- $L1mgmtGroupSubRoleAssignments = $baseRoleAssignments
-
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC -eq $true) {
- foreach ($L1mgmtGroupSubRoleAssignmentOnRg in $L1mgmtGroupSubRoleAssignments.where( { $_.id -match "/subscriptions/$($scopeId)/resourcegroups/" } )) {
- if (-not ($htCacheAssignmentsRBACOnResourceGroupsAndResources).($L1mgmtGroupSubRoleAssignmentOnRg.id)) {
-
- $roleDefinitionId = $L1mgmtGroupSubRoleAssignmentOnRg.properties.roleDefinitionId
- $roleDefinitionIdGuid = $roleDefinitionId -replace '.*/'
-
- if (-not ($htCacheDefinitionsRole).($roleDefinitionIdGuid)) {
- $roleAssignmentsRoleDefinition = ''
- $roleDefinitionName = "'This roleDefinition likely was deleted although a roleAssignment existed'"
- }
- else {
- $roleAssignmentsRoleDefinition = ($htCacheDefinitionsRole).($roleDefinitionIdGuid)
- $roleDefinitionName = $roleAssignmentsRoleDefinition.Name
- }
-
- #assignment
- $arrayRoleAssignment = [System.Collections.ArrayList]@()
- $null = $arrayRoleAssignment.Add([PSCustomObject]@{
- RoleAssignmentId = $L1mgmtGroupSubRoleAssignmentOnRg.id
- Scope = $L1mgmtGroupSubRoleAssignmentOnRg.properties.scope
- RoleDefinitionName = $roleDefinitionName
- RoleDefinitionId = $L1mgmtGroupSubRoleAssignmentOnRg.properties.roleDefinitionId -replace '.*/'
- ObjectId = $L1mgmtGroupSubRoleAssignmentOnRg.properties.principalId
- })
-
- ($script:htCacheAssignmentsRBACOnResourceGroupsAndResources).($L1mgmtGroupSubRoleAssignmentOnRg.id) = $arrayRoleAssignment
- }
- }
- }
-
- if ($azAPICallConf['htParameters'].LargeTenant -eq $true -or $azAPICallConf['htParameters'].RBACAtScopeOnly -eq $true) {
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC -eq $false) {
- $assignmentsScope = $L1mgmtGroupSubRoleAssignments
- }
- else {
- $assignmentsScope = $L1mgmtGroupSubRoleAssignments.where( { $_.properties.Scope -eq "/subscriptions/$($scopeId)" } )
- }
- }
- else {
- if ($azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC -eq $false) {
- $assignmentsScope = $L1mgmtGroupSubRoleAssignments
- }
- else {
- $assignmentsScope = $L1mgmtGroupSubRoleAssignments.where( { $_.id -notmatch "/subscriptions/$($scopeId)/resourcegroups/" } )
- }
- }
-
- foreach ($L1mgmtGroupSubRoleAssignment in $assignmentsScope) {
-
- $roleAssignmentId = ($L1mgmtGroupSubRoleAssignment.id).ToLower()
- $roleDefinitionId = $L1mgmtGroupSubRoleAssignment.properties.roleDefinitionId
- $roleDefinitionIdGuid = $roleDefinitionId -replace '.*/'
-
- if (-not ($htCacheDefinitionsRole).($roleDefinitionIdGuid)) {
- $roleAssignmentsRoleDefinition = ''
- $roleDefinitionName = "'This roleDefinition likely was deleted although a roleAssignment existed'"
- }
- else {
- $roleAssignmentsRoleDefinition = ($htCacheDefinitionsRole).($roleDefinitionIdGuid)
- $roleDefinitionName = $roleAssignmentsRoleDefinition.Name
- }
-
- $roleAssignmentIdentityObjectId = $L1mgmtGroupSubRoleAssignment.properties.principalId
- $roleAssignmentIdentityObjectType = $htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).type
- $roleAssignmentScope = $L1mgmtGroupSubRoleAssignment.properties.scope
- $roleAssignmentScopeName = $roleAssignmentScope -replace '.*/'
-
- if ($roleAssignmentScope -like '/subscriptions/*' -and $roleAssignmentScope -notlike '/subscriptions/*/resourcegroups/*') {
- $roleAssignmentScopeType = 'Sub'
- $roleAssignmentScopeRG = ''
- $roleAssignmentScopeRes = ''
- }
- if ($roleAssignmentScope -like '/subscriptions/*/resourcegroups/*' -and $roleAssignmentScope -notlike '/subscriptions/*/resourcegroups/*/providers*') {
- $roleAssignmentScopeType = 'RG'
- $roleAssignmentScopeSplit = $roleAssignmentScope.Split('/')
- $roleAssignmentScopeRG = $roleAssignmentScopeSplit[4]
- $roleAssignmentScopeRes = ''
- }
- if ($roleAssignmentScope -like '/subscriptions/*/resourcegroups/*/providers*') {
- $roleAssignmentScopeType = 'Res'
- $roleAssignmentScopeSplit = $roleAssignmentScope.Split('/')
- $roleAssignmentScopeRG = $roleAssignmentScopeSplit[4]
- $roleAssignmentScopeRes = $roleAssignmentScopeSplit[8]
- }
-
- if ($htRoleAssignmentsPIM.($roleAssignmentId)) {
- $hlperPim = $htRoleAssignmentsPIM.($roleAssignmentId)
- $pim = 'true'
- $pimAssignmentType = $hlperPim.assignmentType
- $pimSlotStart = $($hlperPim.startDateTime)
- if ($hlperPim.endDateTime) {
- $pimSlotEnd = $($hlperPim.endDateTime)
- }
- else {
- $pimSlotEnd = 'eternity'
- }
- }
- else {
- $pim = 'false'
- $pimAssignmentType = ''
- $pimSlotStart = ''
- $pimSlotEnd = ''
- }
-
- if ($roleAssignmentId -like "/subscriptions/$($scopeId)/*") {
-
- #assignment
- $splitAssignment = ($roleAssignmentId).Split('/')
- $arrayRoleAssignment = [System.Collections.ArrayList]@()
- $null = $arrayRoleAssignment.Add([PSCustomObject]@{
- RoleAssignmentId = $roleAssignmentId
- Scope = $L1mgmtGroupSubRoleAssignment.properties.scope
- DisplayName = $htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).displayName
- SignInName = $htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).signInName
- RoleDefinitionName = $roleDefinitionName
- RoleDefinitionId = $L1mgmtGroupSubRoleAssignment.properties.roleDefinitionId -replace '.*/'
- ObjectId = $L1mgmtGroupSubRoleAssignment.properties.principalId
- ObjectType = $htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).type
- PIM = $pim
- })
-
- $htTemp = @{}
- $htTemp.Assignment = $arrayRoleAssignment
+ $NoDefinitionInsightsDedicatedHTML,
- $htTemp.AssignmentScopeTenMgSubRgRes = $roleAssignmentScopeType
- if ($roleAssignmentScopeType -eq 'Sub') {
- $htTemp.AssignmentScopeId = [string]$splitAssignment[2]
- }
- if ($roleAssignmentScopeType -eq 'RG') {
- $htTemp.AssignmentScopeId = "$($splitAssignment[2])/$($splitAssignment[4])"
- }
- if ($roleAssignmentScopeType -eq 'Res') {
- $htTemp.AssignmentScopeId = "$($splitAssignment[2])/$($splitAssignment[4])/$($splitAssignment[8])"
- $htTemp.ResourceType = "$($splitAssignment[6])-$($splitAssignment[7])"
- }
- ($script:htCacheAssignmentsRole).($roleAssignmentId) = $htTemp
- }
+ [switch]
+ $NoStorageAccountAccessAnalysis,
+ [array]
+ $StorageAccountAccessAnalysisSubscriptionTags = @('undefined'),
- if (($htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).displayName).length -eq 0) {
- $roleAssignmentIdentityDisplayname = 'n/a'
- }
- else {
- if ($htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).type -eq 'User') {
- if ($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData -eq $false) {
- $roleAssignmentIdentityDisplayname = $htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).displayName
- }
- else {
- $roleAssignmentIdentityDisplayname = 'scrubbed'
- }
- }
- else {
- $roleAssignmentIdentityDisplayname = $htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).displayName
- }
- }
- if (-not $htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).signInName) {
- $roleAssignmentIdentitySignInName = 'n/a'
- }
- else {
- if ($htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).type -eq 'User') {
- if ($azAPICallConf['htParameters'].DoNotShowRoleAssignmentsUserData -eq $false) {
- $roleAssignmentIdentitySignInName = $htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).signInName
- }
- else {
- $roleAssignmentIdentitySignInName = 'scrubbed'
- }
- }
- else {
- $roleAssignmentIdentitySignInName = $htPrincipals.($L1mgmtGroupSubRoleAssignment.properties.principalId).signInName
- }
- }
+ [array]
+ $StorageAccountAccessAnalysisStorageAccountTags = @('undefined'),
- $roleSecurityCustomRoleOwner = 0
- if (($htCacheDefinitionsRole).$($roleDefinitionIdGuid).Actions -eq '*' -and ((($htCacheDefinitionsRole).$($roleDefinitionIdGuid).NotActions)).length -eq 0 -and ($htCacheDefinitionsRole).$($roleDefinitionIdGuid).IsCustom -eq $True) {
- $roleSecurityCustomRoleOwner = 1
- }
- $roleSecurityOwnerAssignmentSP = 0
- if ((($htCacheDefinitionsRole).$($roleDefinitionIdGuid).Id -eq '8e3af657-a8ff-443c-a75c-2fe8c4bcb635' -and $roleAssignmentIdentityObjectType -eq 'ServicePrincipal') -or (($htCacheDefinitionsRole).$($roleDefinitionIdGuid).Actions -eq '*' -and ((($htCacheDefinitionsRole).$($roleDefinitionIdGuid).NotActions)).length -eq 0 -and ($htCacheDefinitionsRole).$($roleDefinitionIdGuid).IsCustom -eq $True -and $roleAssignmentIdentityObjectType -eq 'ServicePrincipal')) {
- $roleSecurityOwnerAssignmentSP = 1
- }
+ [switch]
+ $GitHubActionsOIDC,
- $createdBy = ''
- $createdOn = ''
- $createdOnUnformatted = $null
- $updatedBy = ''
- $updatedOn = ''
+ [switch]
+ $NoNetwork,
- if ($L1mgmtGroupSubRoleAssignment.properties.createdBy) {
- $createdBy = $L1mgmtGroupSubRoleAssignment.properties.createdBy
- }
- if ($L1mgmtGroupSubRoleAssignment.properties.createdOn) {
- $createdOn = $L1mgmtGroupSubRoleAssignment.properties.createdOn
- }
- if ($L1mgmtGroupSubRoleAssignment.properties.updatedBy) {
- $updatedBy = $L1mgmtGroupSubRoleAssignment.properties.updatedBy
- }
- if ($L1mgmtGroupSubRoleAssignment.properties.updatedOn) {
- $updatedOn = $L1mgmtGroupSubRoleAssignment.properties.updatedOn
- }
- $createdOnUnformatted = $L1mgmtGroupSubRoleAssignment.properties.createdOn
-
- $addRowToTableDone = $true
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $childMgDisplayName `
- -mgId $childMgId `
- -mgParentId $childMgParentId `
- -mgParentName $childMgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult `
- -Subscription $scopeDisplayName `
- -SubscriptionId $scopeId `
- -SubscriptionQuotaId $subscriptionQuotaId `
- -SubscriptionState $subscriptionState `
- -SubscriptionASCSecureScore $subscriptionASCSecureScore `
- -SubscriptionTags $subscriptionTags `
- -SubscriptionTagsCount $subscriptionTagsCount `
- -RoleDefinitionId $roleDefinitionIdGuid `
- -RoleDefinitionName $roleDefinitionName `
- -RoleIsCustom $roleAssignmentsRoleDefinition.IsCustom `
- -RoleAssignableScopes ($roleAssignmentsRoleDefinition.AssignableScopes -join "$CsvDelimiterOpposite ") `
- -RoleActions ($roleAssignmentsRoleDefinition.Actions -join "$CsvDelimiterOpposite ") `
- -RoleNotActions ($roleAssignmentsRoleDefinition.NotActions -join "$CsvDelimiterOpposite ") `
- -RoleDataActions ($roleAssignmentsRoleDefinition.DataActions -join "$CsvDelimiterOpposite ") `
- -RoleNotDataActions ($roleAssignmentsRoleDefinition.NotDataActions -join "$CsvDelimiterOpposite ") `
- -RoleCanDoRoleAssignments $roleAssignmentsRoleDefinition.RoleCanDoRoleAssignments `
- -RoleAssignmentIdentityDisplayname $roleAssignmentIdentityDisplayname `
- -RoleAssignmentIdentitySignInName $roleAssignmentIdentitySignInName `
- -RoleAssignmentIdentityObjectId $roleAssignmentIdentityObjectId `
- -RoleAssignmentIdentityObjectType $roleAssignmentIdentityObjectType `
- -RoleAssignmentId $roleAssignmentId `
- -RoleAssignmentScope $roleAssignmentScope `
- -RoleAssignmentScopeName $roleAssignmentScopeName `
- -RoleAssignmentScopeRG $roleAssignmentScopeRG `
- -RoleAssignmentScopeRes $roleAssignmentScopeRes `
- -RoleAssignmentScopeType $roleAssignmentScopeType `
- -RoleAssignmentCreatedBy $createdBy `
- -RoleAssignmentCreatedOn $createdOn `
- -RoleAssignmentCreatedOnUnformatted $createdOnUnformatted `
- -RoleAssignmentUpdatedBy $updatedBy `
- -RoleAssignmentUpdatedOn $updatedOn `
- -RoleAssignmentsLimit $roleAssignmentsUsage.roleAssignmentsLimit `
- -RoleAssignmentsCount $roleAssignmentsUsage.roleAssignmentsCurrentCount `
- -RoleSecurityCustomRoleOwner $roleSecurityCustomRoleOwner `
- -RoleSecurityOwnerAssignmentSP $roleSecurityOwnerAssignmentSP `
- -RoleAssignmentPIM $pim `
- -RoleAssignmentPIMAssignmentType $pimAssignmentType `
- -RoleAssignmentPIMSlotStart $pimSlotStart `
- -RoleAssignmentPIMSlotEnd $pimSlotEnd
- }
+ [int]
+ $NetworkSubnetIPAddressUsageCriticalPercentage = 80,
- $returnObject = @{}
- if ($addRowToTableDone) {
- $returnObject.'addRowToTableDone' = @{}
- }
- return $returnObject
-}
-$funcDataCollectionRoleAssignmentsSub = $function:dataCollectionRoleAssignmentsSub.ToString()
-
-function dataCollectionClassicAdministratorsSub {
- [CmdletBinding()]Param(
- [string]$scopeId,
- [string]$scopeDisplayName,
- [string]$subscriptionMgPath,
- $subscriptionQuotaId
- )
-
- $apiEndPoint = $azAPICallConf['azAPIEndpointUrls'].ARM
- $api = "/subscriptions/$($scopeId)/providers/Microsoft.Authorization/classicAdministrators"
- $apiVersion = '?api-version=2015-07-01'
- $uri = $apiEndPoint + $api + $apiVersion
- $azAPICallPayload = @{
- uri = $uri
- method = 'GET'
- currentTask = "classicAdministrators '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- AzAPICallConfiguration = $azAPICallConf
- }
+ [switch]
+ $ShowRunIdentifier,
- $AzApiCallResult = AzAPICall @azAPICallPayload
- if ($AzApiCallResult -ne 'ClassicAdministratorListFailed') {
- $arrayClassicAdministrators = [System.Collections.ArrayList]@()
- foreach ($roleAll in $AzApiCallResult) {
- $splitPropertiesRole = $roleAll.properties.role.Split(';')
- foreach ($role in $splitPropertiesRole) {
- $null = $arrayClassicAdministrators.Add([PSCustomObject]@{
- Subscription = $scopeDisplayName
- SubscriptionId = $scopeId
- SubscriptionMgPath = $subscriptionMgPath
- Identity = $roleAll.properties.emailAddress
- Role = $role
- Id = $roleAll.id
- })
- }
- }
- $script:htClassicAdministrators.($scopeId) = @{}
- $script:htClassicAdministrators.($scopeId).ClassicAdministrators = $arrayClassicAdministrators
- }
-}
-$funcDataCollectionClassicAdministratorsSub = $function:dataCollectionClassicAdministratorsSub.ToString()
-
-#endregion functions4DataCollection
-#region HTML
-function HierarchyMgHTML($mgChild) {
- $mgDetails = $htMgDetails.($mgChild).details
- $mgName = $mgDetails.mgName
- $mgId = $mgDetails.MgId
-
- if ($mgId -eq ($azAPICallConf['checkContext']).Tenant.Id) {
- if ($mgId -eq $defaultManagementGroupId) {
- $class = "class=`"tenantRootGroup mgnonradius defaultMG`""
- }
- else {
- $class = "class=`"tenantRootGroup mgnonradius`""
- }
- }
- else {
- if ($mgId -eq $defaultManagementGroupId) {
- $class = "class=`"mgnonradius defaultMG`""
- }
- else {
- $class = "class=`"mgnonradius`""
- }
- $liclass = ''
- $liId = ''
- }
- if ($mgName -eq $mgId) {
- $mgNameAndOrId = $mgName -replace '<', '<' -replace '>', '>'
- }
- else {
- $mgNameAndOrId = "$($mgName -replace '<', '<' -replace '>', '>')$mgId "
- }
+ #https://learn.microsoft.com/azure/azure-resource-manager/management/azure-subscription-service-limits#role-based-access-control-limits
+ [int]
+ $LimitRBACCustomRoleDefinitionsTenant = 5000,
- $mgPolicyAssignmentCount = 0
- if ($htMgAtScopePolicyAssignments.($mgId)) {
- $mgPolicyAssignmentCount = $htMgAtScopePolicyAssignments.($mgId).AssignmentsCount
- }
- $mgPolicyPolicySetScopedCount = 0
- if ($htMgAtScopePoliciesScoped.($mgId)) {
- $mgPolicyPolicySetScopedCount = $htMgAtScopePoliciesScoped.($mgId).ScopedCount
- }
- $mgIdRoleAssignmentCount = 0
- if ($htMgAtScopeRoleAssignments.($mgId)) {
- $mgIdRoleAssignmentCount = $htMgAtScopeRoleAssignments.($mgId).AssignmentsCount
- }
- $script:html += @"
-
-
-
+ [int]
+ $LimitRBACRoleAssignmentsManagementGroup = 500,
-
+ #https://learn.microsoft.com/azure/governance/policy/overview#maximum-count-of-azure-policy-objects
+ [int]
+ $LimitPOLICYPolicyAssignmentsManagementGroup = 200,
-
$($mgNameAndOrId)
-
-
-
-"@
- $childMgs = $htMgDetails.($mgId).mgChildren
- if (($childMgs).count -gt 0) {
- $script:html += @'
-
-'@
- foreach ($childMg in $childMgs) {
- HierarchyMgHTML -mgChild $childMg
- }
- HierarchySubForMgHTML -mgChild $mgId
- $script:html += @'
-
-
-'@
- }
- else {
- HierarchySubForMgUlHTML -mgChild $mgId
- $script:html += @'
-
-'@
- }
-}
+ [int]
+ $LimitPOLICYPolicyAssignmentsSubscription = 200,
-function HierarchySubForMgHTML($mgChild) {
- $subscriptions = $htMgDetails.($mgChild).Subscriptions.SubScriptionId
- $subscriptionsCnt = ($subscriptions).count
- $subscriptionsOutOfScopelinked = $outOfScopeSubscriptions.where( { $_.ManagementGroupId -eq $mgChild } )
- $subscriptionsOutOfScopelinkedCnt = ($subscriptionsOutOfScopelinked).count
- Write-Host " Building HierarchyMap for MG '$mgChild', $($subscriptionsCnt) Subscriptions"
- if ($subscriptionsCnt -gt 0 -or $subscriptionsOutOfScopelinkedCnt -gt 0) {
- if ($subscriptionsCnt -gt 0 -and $subscriptionsOutOfScopelinkedCnt -gt 0) {
- $script:html += @"
- $(($subscriptions).count)x
$(($subscriptionsOutOfScopelinked).count)x
-"@
- }
- if ($subscriptionsCnt -gt 0 -and $subscriptionsOutOfScopelinkedCnt -eq 0) {
- $script:html += @"
- $(($subscriptions).count)x
-"@
- }
- if ($subscriptionsCnt -eq 0 -and $subscriptionsOutOfScopelinkedCnt -gt 0) {
- $script:html += @"
- $(($subscriptionsOutOfScopelinked).count)x
-"@
- }
- }
-}
+ [int]
+ $LimitPOLICYPolicyDefinitionsScopedManagementGroup = 500,
-function HierarchySubForMgUlHTML($mgChild) {
- $subscriptions = $htMgDetails.($mgChild).Subscriptions.SubScriptionId
- $subscriptionsCnt = ($subscriptions).count
- $subscriptionsOutOfScopelinked = $outOfScopeSubscriptions.where( { $_.ManagementGroupId -eq $mgChild } )
- $subscriptionsOutOfScopelinkedCnt = ($subscriptionsOutOfScopelinked).count
- Write-Host " Building HierarchyMap for MG '$mgChild', $($subscriptionsCnt) Subscriptions"
- if ($subscriptionsCnt -gt 0 -or $subscriptionsOutOfScopelinkedCnt -gt 0) {
- if ($subscriptionsCnt -gt 0 -and $subscriptionsOutOfScopelinkedCnt -gt 0) {
- $script:html += @"
-
-"@
- }
- if ($subscriptionsCnt -gt 0 -and $subscriptionsOutOfScopelinkedCnt -eq 0) {
- $script:html += @"
-
-"@
- }
- if ($subscriptionsCnt -eq 0 -and $subscriptionsOutOfScopelinkedCnt -gt 0) {
- $script:html += @"
-
-"@
- }
- }
-}
+ [int]
+ $LimitPOLICYPolicyDefinitionsScopedSubscription = 500,
-function processScopeInsights($mgChild, $mgChildOf) {
- $mgDetails = $htMgDetails.($mgChild).details
- $mgName = $mgDetails.mgName
- $mgLevel = $mgDetails.Level
- $mgId = $mgDetails.MgId
+ [int]
+ $LimitPOLICYPolicySetAssignmentsManagementGroup = 200,
- if (-not $NoScopeInsights) {
- if ($mgId -eq $defaultManagementGroupId) {
- $classDefaultMG = 'defaultMG'
- }
- else {
- $classDefaultMG = ''
- }
+ [int]
+ $LimitPOLICYPolicySetAssignmentsSubscription = 200,
- switch ($mgLevel) {
- '0' { $levelSpacing = '| L0 – ' }
- '1' { $levelSpacing = '| – L1 – ' }
- '2' { $levelSpacing = '| – – L2 – ' }
- '3' { $levelSpacing = '| – – – L3 – ' }
- '4' { $levelSpacing = '| – – – – L4 – ' }
- '5' { $levelSpacing = '| – – – – – L5 – ' }
- '6' { $levelSpacing = '| – – – – – – L6 – ' }
- }
+ [int]
+ $LimitPOLICYPolicySetDefinitionsScopedTenant = 2500,
- $mgPath = $htManagementGroupsMgPath.($mgChild).pathDelimited
+ [int]
+ $LimitPOLICYPolicySetDefinitionsScopedManagementGroup = 200,
- $mgLinkedSubsCount = ((($optimizedTableForPathQuery.where( { $_.MgId -eq $mgChild -and -not [String]::IsNullOrEmpty($_.SubscriptionId) } )).SubscriptionId | Get-Unique)).count
- $subscriptionsOutOfScopelinkedCount = ($outOfScopeSubscriptions.where( { $_.ManagementGroupId -eq $mgChild } )).count
- if ($mgLinkedSubsCount -gt 0 -and $subscriptionsOutOfScopelinkedCount -eq 0) {
- $subInfo = " $mgLinkedSubsCount"
- }
- if ($mgLinkedSubsCount -gt 0 -and $subscriptionsOutOfScopelinkedCount -gt 0) {
- $subInfo = " $mgLinkedSubsCount $subscriptionsOutOfScopelinkedCount"
- }
- if ($mgLinkedSubsCount -eq 0 -and $subscriptionsOutOfScopelinkedCount -gt 0) {
- $subInfo = " $subscriptionsOutOfScopelinkedCount"
- }
- if ($mgLinkedSubsCount -eq 0 -and $subscriptionsOutOfScopelinkedCount -eq 0) {
- $subInfo = " "
- }
+ [int]
+ $LimitPOLICYPolicySetDefinitionsScopedSubscription = 200,
- if ($mgName -eq $mgId) {
- $mgNameAndOrId = "$($mgName -replace '<', '<' -replace '>', '>')"
- }
- else {
- $mgNameAndOrId = "$($mgName -replace '<', '<' -replace '>', '>') ($mgId)"
- }
+ #https://learn.microsoft.com/azure/azure-resource-manager/management/azure-subscription-service-limits#subscription-limits
+ [int]
+ $LimitResourceGroups = 980,
- $script:html += @"
-$levelSpacing $mgNameAndOrId $subInfo
-
-
- Highlight Management Group in HierarchyMap
-"@
- if ($mgId -eq $defaultManagementGroupId) {
- $script:html += @'
- Default Management Group docs
-'@
- }
- $script:html += @"
-Management Group Name: $($mgName -replace '<', '<' -replace '>', '>')
-Management Group Id: $mgId
-Management Group Path: $mgPath
-"@
- }
- processScopeInsightsMgOrSub -mgOrSub 'mg' -mgchild $mgId
- processScopeInsightsMGSubs -mgChild $mgId
- $childMgs = $htMgDetails.($mgId).mgChildren
- if (($childMgs).count -gt 0) {
- foreach ($childMg in $childMgs) {
- processScopeInsights -mgChild $childMg -mgChildOf $mgId
- }
- }
-}
+ [int]
+ $LimitTagsSubscription = 50,
-function processScopeInsightsMGSubs($mgChild) {
- $subscriptions = $htMgDetails.($mgChild).Subscriptions
- $subscriptionLinkedCount = ($subscriptions).count
- $subscriptionsOutOfScopelinked = $outOfScopeSubscriptions.where( { $_.ManagementGroupId -eq $mgChild } )
- $subscriptionsOutOfScopelinkedCount = ($subscriptionsOutOfScopelinked).count
- if ($subscriptionsOutOfScopelinkedCount -gt 0) {
- $subscriptionsOutOfScopelinkedDetail = "($($subscriptionsOutOfScopelinkedCount) out-of-scope)"
- }
- else {
- $subscriptionsOutOfScopelinkedDetail = ''
- }
- Write-Host " Building ScopeInsights MG '$mgChild', $subscriptionLinkedCount Subscriptions"
-
- if ($subscriptionLinkedCount -gt 0) {
- if (-not $NoScopeInsights) {
- $script:html += @"
-
-
- $subscriptionLinkedCount Subscriptions linked $subscriptionsOutOfScopelinkedDetail
-
-"@
- }
- foreach ($subEntry in $subscriptions | Sort-Object -Property subscription, subscriptionId) {
- #$subPath = $htSubscriptionsMgPath.($subEntry.subscriptionId).pathDelimited
- if ($subscriptionLinkedCount -gt 1) {
- if (-not $NoScopeInsights) {
- $script:html += @"
-
$($subEntry.subscription -replace '<', '<' -replace '>', '>') ($($subEntry.subscriptionId))
-
-"@
- }
- }
- #exactly 1
- else {
- if (-not $NoScopeInsights) {
- $script:html += @"
-
$($subEntry.subscription -replace '<', '<' -replace '>', '>') ($($subEntry.subscriptionId))
-"@
- }
- }
- if (-not $NoScopeInsights) {
- $script:html += @"
-
- Highlight Subscription in HierarchyMap
-"@
- }
+ [array]
+ $MSTenantIds = @('2f4a9838-26b7-47ee-be60-ccc1fdec5953', '33e01921-4d64-4f8c-a055-5bdaffd5e33d'),
- if (-not $azAPICallConf['htParameters'].ManagementGroupsOnly) {
- processScopeInsightsMgOrSub -mgOrSub 'sub' -subscriptionId $subEntry.subscriptionId -subscriptionsMgId $mgChild
- }
+ [array]
+ $ValidPolicyEffects = @('append', 'audit', 'auditIfNotExists', 'deny', 'denyAction', 'deployIfNotExists', 'modify', 'manual', 'disabled', 'EnforceRegoPolicy', 'enforceSetting')
+)
- if (-not $NoScopeInsights) {
- $script:html += @'
-
-'@
- }
- if ($subscriptionLinkedCount -gt 1) {
- if (-not $NoScopeInsights) {
- $script:html += @'
-
-'@
- }
- }
- }
- if (-not $NoScopeInsights) {
- $script:html += @'
-
-'@
- }
+$Error.clear()
+$ErrorActionPreference = 'Stop'
+#removeNoise
+$ProgressPreference = 'SilentlyContinue'
+Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings 'true'
- }
- else {
- if (-not $NoScopeInsights) {
- $script:html += @"
-
-
- $subscriptionLinkedCount Subscriptions linked $subscriptionsOutOfScopelinkedDetail
-"@
- }
- }
- if (-not $NoScopeInsights) {
- $script:html += @'
-
-
+#start
+$startAzGovViz = Get-Date
+$startTime = Get-Date -Format 'dd-MMM-yyyy HH:mm:ss'
+Write-Host "Start Azure Governance Visualizer $($startTime) (#$($ProductVersion))"
-
-
-'@
- }
+if ($ManagementGroupId -match ' ') {
+ Write-Host "Provided Management Group ID: '$($ManagementGroupId)'" -ForegroundColor Yellow
+ Write-Host 'The Management Group ID may not contain spaces - provide the Management Group ID, not the displayName.' -ForegroundColor DarkRed
+ throw 'Management Group ID validation failed!'
}
-#endregion HTML
+
+#region Functions
+. ".\$($ScriptPath)\functions\validateLeastPrivilegeForUser.ps1"
+. ".\$($ScriptPath)\functions\getPolicyRemediation.ps1"
+. ".\$($ScriptPath)\functions\getPolicyHash.ps1"
+. ".\$($ScriptPath)\functions\detectPolicyEffect.ps1"
+. ".\$($ScriptPath)\functions\exportResourceLocks.ps1"
+. ".\$($ScriptPath)\functions\processHierarchyMapOnlyCustomData.ps1"
+. ".\$($ScriptPath)\functions\processPrivateEndpoints.ps1"
+. ".\$($ScriptPath)\functions\processNetwork.ps1"
+. ".\$($ScriptPath)\functions\processStorageAccountAnalysis.ps1"
+. ".\$($ScriptPath)\functions\processALZPolicyVersionChecker.ps1"
+. ".\$($ScriptPath)\functions\getPIMEligible.ps1"
+. ".\$($ScriptPath)\functions\testGuid.ps1"
+. ".\$($ScriptPath)\functions\apiCallTracking.ps1"
+. ".\$($ScriptPath)\functions\addRowToTable.ps1"
+. ".\$($ScriptPath)\functions\testPowerShellVersion.ps1"
+. ".\$($ScriptPath)\functions\setOutput.ps1"
+. ".\$($ScriptPath)\functions\setTranscript.ps1"
+. ".\$($ScriptPath)\functions\verifyModules3rd.ps1"
+. ".\$($ScriptPath)\functions\checkAzGovVizVersion.ps1"
+. ".\$($ScriptPath)\functions\handleCloudEnvironment.ps1"
+. ".\$($ScriptPath)\functions\addHtParameters.ps1"
+. ".\$($ScriptPath)\functions\selectMg.ps1"
+. ".\$($ScriptPath)\functions\validateAccess.ps1"
+. ".\$($ScriptPath)\functions\getEntities.ps1"
+. ".\$($ScriptPath)\functions\setBaseVariablesMG.ps1"
+. ".\$($ScriptPath)\functions\getTenantDetails.ps1"
+. ".\$($ScriptPath)\functions\getDefaultManagementGroup.ps1"
+. ".\$($ScriptPath)\functions\runInfo.ps1"
+. ".\$($ScriptPath)\functions\processHierarchyMapOnly.ps1"
+. ".\$($ScriptPath)\functions\getSubscriptions.ps1"
+. ".\$($ScriptPath)\functions\detailSubscriptions.ps1"
+. ".\$($ScriptPath)\functions\getOrphanedResources.ps1"
+. ".\$($ScriptPath)\functions\getMDfCSecureScoreMG.ps1"
+. ".\$($ScriptPath)\functions\getConsumption.ps1"
+. ".\$($ScriptPath)\functions\cacheBuiltIn.ps1"
+. ".\$($ScriptPath)\functions\prepareData.ps1"
+. ".\$($ScriptPath)\functions\getGroupmembers.ps1"
+. ".\$($ScriptPath)\functions\processAADGroups.ps1"
+. ".\$($ScriptPath)\functions\processApplications.ps1"
+. ".\$($ScriptPath)\functions\processManagedIdentities.ps1"
+. ".\$($ScriptPath)\functions\createTagList.ps1"
+. ".\$($ScriptPath)\functions\getResourceDiagnosticsCapability.ps1"
+. ".\$($ScriptPath)\functions\getFileNaming.ps1"
+. ".\$($ScriptPath)\functions\resolveObjectIds.ps1"
+. ".\$($ScriptPath)\functions\namingValidation.ps1"
+. ".\$($ScriptPath)\functions\removeInvalidFileNameChars.ps1"
+. ".\$($ScriptPath)\functions\addIndexNumberToArray.ps1"
+. ".\$($ScriptPath)\functions\processDiagramMermaid.ps1"
+. ".\$($ScriptPath)\functions\buildMD.ps1"
+. ".\$($ScriptPath)\functions\buildTree.ps1"
+. ".\$($ScriptPath)\functions\buildJSON.ps1"
+. ".\$($ScriptPath)\functions\buildPolicyAllJSON.ps1"
+. ".\$($ScriptPath)\functions\stats.ps1"
+#Region dataCollectionFunctions
+. ".\$($ScriptPath)\functions\dataCollection\dataCollectionFunctions.ps1"
+. ".\$($ScriptPath)\functions\processDataCollection.ps1"
+. ".\$($ScriptPath)\functions\exportBaseCSV.ps1"
+. ".\$($ScriptPath)\functions\html\htmlFunctions.ps1"
+. ".\$($ScriptPath)\functions\processTenantSummary.ps1"
+. ".\$($ScriptPath)\functions\processDefinitionInsights.ps1"
+. ".\$($ScriptPath)\functions\processScopeInsightsMgOrSub.ps1"
+. ".\$($ScriptPath)\functions\showMemoryUsage.ps1"
+#EndRegion dataCollectionFunctions
#endregion Functions
$funcAddRowToTable = $function:addRowToTable.ToString()
@@ -34230,36 +1156,83 @@ if (-not $HierarchyMapOnly) {
#region Getting Available Private Endpoint Types
$startGetAvailablePrivateEndpointTypes = Get-Date
- $currentTask = 'Getting Locations'
- Write-Host $currentTask
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($azAPICallConf['checkcontext'].Subscription.Id)/locations?api-version=2020-01-01"
- $method = 'GET'
- $getLocations = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
- Write-Host " Returned $($getLocations.Count) locations"
-
- Write-Host "Getting 'Available Private Endpoint Types' for $($getLocations.Count) locations"
- $getLocations | ForEach-Object -Parallel {
- $location = $_
- $azAPICallConf = $using:azAPICallConf
- $htAvailablePrivateEndpointTypes = $using:htAvailablePrivateEndpointTypes
- $currentTask = "Getting 'Available Private Endpoint Types' for location $($location.name)"
- #Write-Host $currentTask
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($azAPICallConf['checkcontext'].Subscription.Id)/providers/Microsoft.Network/locations/$($location.name)/availablePrivateEndpointTypes?api-version=2022-07-01"
+ $subsToProcessForGettingPrivateEndpointTypes = [System.Collections.ArrayList]@()
+ $prioCounter = 0
+ foreach ($subscription in $subsToProcessInCustomDataCollection) {
+ $prioCounter++
+ if ($subscription.subscriptionId -eq $azAPICallConf['checkcontext'].Subscription.Id) {
+ $null = $subsToProcessForGettingPrivateEndpointTypes.Add([PSCustomObject]@{
+ subscriptionInfo = $subscription
+ prio = 0
+ })
+ }
+ else {
+ $null = $subsToProcessForGettingPrivateEndpointTypes.Add([PSCustomObject]@{
+ subscriptionInfo = $subscription
+ prio = $prioCounter
+ })
+ }
+ }
+
+ foreach ($subscription in $subsToProcessForGettingPrivateEndpointTypes | Sort-Object -Property prio) {
+
+ if ($privateEndpointAvailabilityCheckCompleted) {
+ continue
+ }
+
+ $subscriptionId = $subscription.subscriptionInfo.subscriptionId
+ $subscriptionName = $subscription.subscriptionInfo.subscriptionName
+
+ $currentTask = "Getting Locations for Subscription '$($subscriptionName)' ($($subscriptionId))"
+ Write-Host $currentTask
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($subscriptionId)/locations?api-version=2020-01-01"
$method = 'GET'
- $availablePrivateEndpointTypes = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -skipOnErrorCode 400, 409
- Write-Host " Returned $($availablePrivateEndpointTypes.Count) 'Available Private Endpoint Types' for location $($location.name)"
- foreach ($availablePrivateEndpointType in $availablePrivateEndpointTypes) {
- if (-not $htAvailablePrivateEndpointTypes.(($availablePrivateEndpointType.resourceName).ToLower())) {
- $script:htAvailablePrivateEndpointTypes.(($availablePrivateEndpointType.resourceName).ToLower()) = @{}
+ $getLocations = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
+ Write-Host " Returned $($getLocations.Count) locations"
+
+ Write-Host "Getting 'Available Private Endpoint Types' for Subscription '$($subscriptionName)' ($($subscriptionId)) for $($getLocations.Count) locations"
+
+ $batchSize = [math]::ceiling($getLocations.Count / $ThrottleLimit)
+ Write-Host "Optimal batch size: $($batchSize)"
+ $counterBatch = [PSCustomObject] @{ Value = 0 }
+ $getLocationsBatch = ($getLocations) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ Write-Host "Processing data in $($getLocationsBatch.Count) batches"
+
+ $getLocationsBatch | ForEach-Object -Parallel {
+ $subscriptionId = $using:subscriptionId
+ $azAPICallConf = $using:azAPICallConf
+ $htAvailablePrivateEndpointTypes = $using:htAvailablePrivateEndpointTypes
+
+ foreach ($location in $_.Group) {
+ $currentTask = "Getting 'Available Private Endpoint Types' for location $($location.name)"
+ #Write-Host $currentTask
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($subscriptionId)/providers/Microsoft.Network/locations/$($location.name)/availablePrivateEndpointTypes?api-version=2022-07-01"
+ $method = 'GET'
+ $availablePrivateEndpointTypes = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -skipOnErrorCode 400, 409
+ Write-Host " Returned $($availablePrivateEndpointTypes.Count) 'Available Private Endpoint Types' for location $($location.name)"
+ foreach ($availablePrivateEndpointType in $availablePrivateEndpointTypes) {
+ if (-not $htAvailablePrivateEndpointTypes.(($availablePrivateEndpointType.resourceName).ToLower())) {
+ $script:htAvailablePrivateEndpointTypes.(($availablePrivateEndpointType.resourceName).ToLower()) = @{}
+ }
+ }
}
+ } -ThrottleLimit $ThrottleLimit
+
+ if ($htAvailablePrivateEndpointTypes.Keys.Count -gt 0) {
+ #Write-Host " Created ht for $($htAvailablePrivateEndpointTypes.Keys.Count) 'Available Private Endpoint Types'"
+ $privateEndpointAvailabilityCheckCompleted = $true
+ }
+ else {
+ Write-Host " $($htAvailablePrivateEndpointTypes.Keys.Count) 'Available Private Endpoint Types' - likely the Resource Provider 'Microsoft.Network' is not registered - trying next available subscription"
+ $privateEndpointAvailabilityCheckCompleted = $false
}
- } -ThrottleLimit $ThrottleLimit
+ }
if ($htAvailablePrivateEndpointTypes.Keys.Count -gt 0) {
Write-Host " Created ht for $($htAvailablePrivateEndpointTypes.Keys.Count) 'Available Private Endpoint Types'"
}
else {
- $throwmsg = "$($htAvailablePrivateEndpointTypes.Keys.Count) 'Available Private Endpoint Types' - Please use another Subscription for the AzContext (current subscriptionId: '$($azAPICallConf['checkcontext'].Subscription.Id)') -> use parameter: -SubscriptionId4AzContext ''"
+ $throwmsg = "$($htAvailablePrivateEndpointTypes.Keys.Count) 'Available Private Endpoint Types' - Checked for $($subsToProcessForGettingPrivateEndpointTypes.Count) Subscriptions with no success. Make sure that for at least one Subscription the Resource Provider 'Microsoft.Network' is registered. Once you registered the Resource Provider for Subscription 'subscriptionEnabled' it may be a good idea to use the parameter: -SubscriptionId4AzContext ''"
Write-Host $throwmsg -ForegroundColor DarkRed
Throw $throwmsg
}
diff --git a/pwsh/dev/functions/buildJSON.ps1 b/pwsh/dev/functions/buildJSON.ps1
index 8166d335..71b7e65f 100644
--- a/pwsh/dev/functions/buildJSON.ps1
+++ b/pwsh/dev/functions/buildJSON.ps1
@@ -32,9 +32,11 @@ function buildJSON {
$htSubRGPolicyAssignments.($subId) = @{}
}
if (-not $htSubRGPolicyAssignments.($subId).PolicyAssignments) {
- $htSubRGPolicyAssignments.($subId).PolicyAssignments = @()
+ $htSubRGPolicyAssignments.($subId).PolicyAssignments = [System.Collections.ArrayList]@()
+ }
+ foreach ($rgpafg in $rgpa.group) {
+ $null = $htSubRGPolicyAssignments.($subId).PolicyAssignments.Add($rgpafg)
}
- $htSubRGPolicyAssignments.($subId).PolicyAssignments += $rgpa.group
}
}
}
@@ -54,9 +56,11 @@ function buildJSON {
$htSubRGRoleAssignments.($subId) = @{}
}
if (-not $htSubRGRoleAssignments.($subId).RoleAssignments) {
- $htSubRGRoleAssignments.($subId).RoleAssignments = @()
+ $htSubRGRoleAssignments.($subId).RoleAssignments = [System.Collections.ArrayList]@()
+ }
+ foreach ($rgrafg in $rgra.group) {
+ $null = $htSubRGRoleAssignments.($subId).RoleAssignments.Add($rgrafg)
}
- $htSubRGRoleAssignments.($subId).RoleAssignments += $rgra.group
}
#res
@@ -307,6 +311,9 @@ function buildJSON {
}
if (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)") {
+ if (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)$($DirectorySeparatorChar)Definitions") {
+ $createDefinitionsLegacyAndNew = $true
+ }
Write-Host ' Cleaning old state (Pipeline only)'
Remove-Item -Recurse -Force "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)"
}
@@ -328,6 +335,7 @@ function buildJSON {
}
$null = New-Item -Name "$($JSONPath)$($DirectorySeparatorChar)Definitions" -ItemType directory -Path $outputPath
+ $null = New-Item -Name "$($JSONPath)$($DirectorySeparatorChar)Definitions_tracking" -ItemType directory -Path $outputPath
@@ -341,16 +349,47 @@ function buildJSON {
$null = New-Item -Name "$($pathRoleDefinitionCustom)" -ItemType directory -Path $outputPath
$null = New-Item -Name "$($pathRoleDefinitionBuiltIn)" -ItemType directory -Path $outputPath
}
+ $pathRoleDefinitionsTracking = "$($JSONPath)$($DirectorySeparatorChar)Definitions_tracking$($DirectorySeparatorChar)RoleDefinitions"
+ if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathRoleDefinitionsTracking)")) {
+ $null = New-Item -Name $pathRoleDefinitionsTracking -ItemType directory -Path $outputPath
+ $pathRoleDefinitionCustomTracking = "$($pathRoleDefinitionsTracking)$($DirectorySeparatorChar)Custom"
+ $pathRoleDefinitionBuiltInTracking = "$($pathRoleDefinitionsTracking)$($DirectorySeparatorChar)BuiltIn"
+ $null = New-Item -Name "$($pathRoleDefinitionCustomTracking)" -ItemType directory -Path $outputPath
+ $null = New-Item -Name "$($pathRoleDefinitionBuiltInTracking)" -ItemType directory -Path $outputPath
+ }
if (($htCacheDefinitionsRole).Keys.Count -gt 0) {
foreach ($roleDefinition in ($htCacheDefinitionsRole).Keys.where( { ($htCacheDefinitionsRole).($_).IsCustom }) | Sort-Object) {
$htJSON.RoleDefinitions.($roleDefinition) = ($htCacheDefinitionsRole).($roleDefinition).Json.properties
$jsonConverted = ($htCacheDefinitionsRole).($roleDefinition).Json.properties | ConvertTo-Json -Depth 99
$jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathRoleDefinitionCustom)$($DirectorySeparatorChar)$(removeInvalidFileNameChars ($htCacheDefinitionsRole).($roleDefinition).Name) ($(($htCacheDefinitionsRole).($roleDefinition).Id)).json" -Encoding utf8
+
+ #if a custom role has multiple assignable scopes, the definition id may vary depending which scope AzGovViz retrieved the definition from, therefore for better change tracking we pack assignablescopes, sort them and use the first entry as id
+
+ if (($htCacheDefinitionsRole).($roleDefinition).Json.properties.assignableScopes.Count -gt 1) {
+ $jsonAdjustment4Tracking = (($htCacheDefinitionsRole).($roleDefinition).Json).psobject.copy()
+ $arrayAssignableScopes = [System.Collections.ArrayList]@()
+ foreach ($assignableScope in $jsonAdjustment4Tracking.properties.assignableScopes) {
+ if ($assignableScope -like '/subscriptions/*') {
+ $null = $arrayAssignableScopes.Add("$($assignableScope)/providers/Microsoft.Authorization/roleDefinitions/$($jsonAdjustment4Tracking.name)")
+ }
+ else {
+ $null = $arrayAssignableScopes.Add("/providers/Microsoft.Authorization/roleDefinitions/$($jsonAdjustment4Tracking.name)")
+ }
+ }
+ $jsonAdjustment4Tracking.id = ($arrayAssignableScopes | Sort-Object)[0]
+ $jsonConvertedTracking = $jsonAdjustment4Tracking | ConvertTo-Json -Depth 99
+ }
+ else {
+ $jsonConvertedTracking = ($htCacheDefinitionsRole).($roleDefinition).Json | ConvertTo-Json -Depth 99
+ }
+ $jsonConvertedTracking | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathRoleDefinitionCustomTracking)$($DirectorySeparatorChar)$(($htCacheDefinitionsRole).($roleDefinition).Id).json" -Encoding utf8
}
foreach ($roleDefinition in ($htCacheDefinitionsRole).Keys.where( { -not ($htCacheDefinitionsRole).($_).IsCustom })) {
- $jsonConverted = ($htCacheDefinitionsRole).($roleDefinition).Json | ConvertTo-Json -Depth 99
+ $jsonConverted = ($htCacheDefinitionsRole).($roleDefinition).Json.properties | ConvertTo-Json -Depth 99
$jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathRoleDefinitionBuiltIn)$($DirectorySeparatorChar)$(removeInvalidFileNameChars ($htCacheDefinitionsRole).($roleDefinition).Name ) ($(($htCacheDefinitionsRole).($roleDefinition).Id)).json" -Encoding utf8
+ $jsonConvertedTracking = ($htCacheDefinitionsRole).($roleDefinition).Json | ConvertTo-Json -Depth 99
+ $jsonConvertedTracking | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathRoleDefinitionBuiltInTracking)$($DirectorySeparatorChar)$(($htCacheDefinitionsRole).($roleDefinition).Id).json" -Encoding utf8
}
}
@@ -360,10 +399,18 @@ function buildJSON {
$pathPolicyDefinitionBuiltIn = "$($pathPolicyDefinitions)$($DirectorySeparatorChar)BuiltIn"
$null = New-Item -Name "$($pathPolicyDefinitionBuiltIn)" -ItemType directory -Path $outputPath
}
+ $pathPolicyDefinitionsTracking = "$($JSONPath)$($DirectorySeparatorChar)Definitions_tracking$($DirectorySeparatorChar)PolicyDefinitions"
+ if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathPolicyDefinitionsTracking)")) {
+ $null = New-Item -Name $pathPolicyDefinitionsTracking -ItemType directory -Path $outputPath
+ $pathPolicyDefinitionBuiltInTracking = "$($pathPolicyDefinitionsTracking)$($DirectorySeparatorChar)BuiltIn"
+ $null = New-Item -Name "$($pathPolicyDefinitionBuiltInTracking)" -ItemType directory -Path $outputPath
+ }
if (($htCacheDefinitionsPolicy).Keys.Count -gt 0) {
foreach ($policyDefinition in ($htCacheDefinitionsPolicy).Keys.where( { ($htCacheDefinitionsPolicy).($_).Type -eq 'BuiltIn' })) {
$jsonConverted = ($htCacheDefinitionsPolicy).($policyDefinition).Json.properties | ConvertTo-Json -Depth 99
$jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathPolicyDefinitionBuiltIn)$($DirectorySeparatorChar)$(removeInvalidFileNameChars ($htCacheDefinitionsPolicy).($policyDefinition).displayName) ($(($htCacheDefinitionsPolicy).($policyDefinition).Json.name)).json" -Encoding utf8
+ $jsonConvertedTracking = ($htCacheDefinitionsPolicy).($policyDefinition).Json | ConvertTo-Json -Depth 99
+ $jsonConvertedTracking | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathPolicyDefinitionBuiltInTracking)$($DirectorySeparatorChar)$(($htCacheDefinitionsPolicy).($policyDefinition).Json.name).json" -Encoding utf8
}
}
@@ -373,10 +420,18 @@ function buildJSON {
$pathPolicySetDefinitionBuiltIn = "$($pathPolicySetDefinitions)$($DirectorySeparatorChar)BuiltIn"
$null = New-Item -Name "$($pathPolicySetDefinitionBuiltIn)" -ItemType directory -Path $outputPath
}
+ $pathPolicySetDefinitionsTracking = "$($JSONPath)$($DirectorySeparatorChar)Definitions_tracking$($DirectorySeparatorChar)PolicySetDefinitions"
+ if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathPolicySetDefinitionsTracking)")) {
+ $null = New-Item -Name $pathPolicySetDefinitionsTracking -ItemType directory -Path $outputPath
+ $pathPolicySetDefinitionBuiltInTracking = "$($pathPolicySetDefinitionsTracking)$($DirectorySeparatorChar)BuiltIn"
+ $null = New-Item -Name "$($pathPolicySetDefinitionBuiltInTracking)" -ItemType directory -Path $outputPath
+ }
if (($htCacheDefinitionsPolicySet).Keys.Count -gt 0) {
foreach ($policySetDefinition in ($htCacheDefinitionsPolicySet).Keys.where( { ($htCacheDefinitionsPolicySet).($_).Type -eq 'BuiltIn' })) {
$jsonConverted = ($htCacheDefinitionsPolicySet).($policySetDefinition).Json.properties | ConvertTo-Json -Depth 99
$jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathPolicySetDefinitionBuiltIn)$($DirectorySeparatorChar)$(removeInvalidFileNameChars ($htCacheDefinitionsPolicySet).($policySetDefinition).displayName) ($(($htCacheDefinitionsPolicySet).($policySetDefinition).Json.name)).json" -Encoding utf8
+ $jsonConverted = ($htCacheDefinitionsPolicySet).($policySetDefinition).Json | ConvertTo-Json -Depth 99
+ $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathPolicySetDefinitionBuiltInTracking)$($DirectorySeparatorChar)$(($htCacheDefinitionsPolicySet).($policySetDefinition).Json.name).json" -Encoding utf8
}
}
@@ -411,6 +466,12 @@ function buildJSON {
$null = New-Item -Name $path -ItemType directory -Path $outputPath
}
$jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($RoleAssignment.Assignment.ObjectType)_$($pim)$($RoleAssignment.Assignment.RoleAssignmentId -replace '.*/').json" -Encoding utf8
+
+ $pathTracking = "$($JSONPath)$($DirectorySeparatorChar)Assignments_tracking$($DirectorySeparatorChar)RoleAssignments$($DirectorySeparatorChar)Tenant"
+ if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)")) {
+ $null = New-Item -Name $pathTracking -ItemType directory -Path $outputPath
+ }
+ $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)$($DirectorySeparatorChar)$($RoleAssignment.Assignment.ObjectType)_$($pim)$($RoleAssignment.Assignment.RoleAssignmentId -replace '.*/').json" -Encoding utf8
}
$htTree.'Tenant'.'ManagementGroups' = [ordered] @{}
@@ -419,6 +480,9 @@ function buildJSON {
if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)$($DirectorySeparatorChar)Assignments")) {
$null = New-Item -Name "$($JSONPath)$($DirectorySeparatorChar)Assignments" -ItemType directory -Path $outputPath
}
+ if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($JSONPath)$($DirectorySeparatorChar)Assignments_tracking")) {
+ $null = New-Item -Name "$($JSONPath)$($DirectorySeparatorChar)Assignments_tracking" -ItemType directory -Path $outputPath
+ }
buildTree -mgId $ManagementGroupId -json $json -prnt "$($JSONPath)$($DirectorySeparatorChar)Tenant"
diff --git a/pwsh/dev/functions/buildTree.ps1 b/pwsh/dev/functions/buildTree.ps1
index 4121ea75..d75fc5d9 100644
--- a/pwsh/dev/functions/buildTree.ps1
+++ b/pwsh/dev/functions/buildTree.ps1
@@ -12,12 +12,13 @@ function buildTree($mgId, $prnt) {
$json.'ManagementGroups' = [ordered]@{}
}
$json = $json.'ManagementGroups'.($getMg.Id) = [ordered]@{}
- foreach ($mgCap in $htJSON.ManagementGroups.($getMg.Id).keys) {
- $json.$mgCap = $htJSON.ManagementGroups.($getMg.Id).$mgCap
+ $mgJson = $htJSON.ManagementGroups.($getMg.Id)
+ foreach ($mgCap in $mgJson.keys) {
+ $json.$mgCap = $mgJson.$mgCap
if ($mgCap -eq 'PolicyDefinitionsCustom') {
$mgCapShort = 'pd'
- foreach ($pdc in $htJSON.ManagementGroups.($getMg.Id).($mgCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($pdc)
+ foreach ($pdc in $mgJson.($mgCap).Keys) {
+ $hlp = $mgJson.($mgCap).($pdc)
if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
$displayName = 'noDisplayNameGiven'
}
@@ -31,12 +32,19 @@ function buildTree($mgId, $prnt) {
$null = New-Item -Name $path -ItemType directory -Path $outputPath
}
$jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
+
+ $jsonConvertedTracking = $hlp | ConvertTo-Json -Depth 99
+ $pathTracking = "$($JSONPath)$($DirectorySeparatorChar)Definitions_tracking$($DirectorySeparatorChar)PolicyDefinitions$($DirectorySeparatorChar)Custom$($DirectorySeparatorChar)Mg$($DirectorySeparatorChar)$($mgNameValid)"
+ if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)")) {
+ $null = New-Item -Name $pathTracking -ItemType directory -Path $outputPath
+ }
+ $jsonConvertedTracking | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)$($DirectorySeparatorChar)$(removeInvalidFileNameChars $hlp.name).json" -Encoding utf8
}
}
if ($mgCap -eq 'PolicySetDefinitionsCustom') {
$mgCapShort = 'psd'
- foreach ($psdc in $htJSON.ManagementGroups.($getMg.Id).($mgCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($psdc)
+ foreach ($psdc in $mgJson.($mgCap).Keys) {
+ $hlp = $mgJson.($mgCap).($psdc)
if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
$displayName = 'noDisplayNameGiven'
}
@@ -50,12 +58,19 @@ function buildTree($mgId, $prnt) {
$null = New-Item -Name $path -ItemType directory -Path $outputPath
}
$jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
+
+ $jsonConvertedTracking = $hlp | ConvertTo-Json -Depth 99
+ $pathTracking = "$($JSONPath)$($DirectorySeparatorChar)Definitions_tracking$($DirectorySeparatorChar)PolicySetDefinitions$($DirectorySeparatorChar)Custom$($DirectorySeparatorChar)Mg$($DirectorySeparatorChar)$($mgNameValid)"
+ if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)")) {
+ $null = New-Item -Name $pathTracking -ItemType directory -Path $outputPath
+ }
+ $jsonConvertedTracking | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)$($DirectorySeparatorChar)$(removeInvalidFileNameChars $hlp.name).json" -Encoding utf8
}
}
if ($mgCap -eq 'PolicyAssignments') {
$mgCapShort = 'pa'
- foreach ($pa in $htJSON.ManagementGroups.($getMg.Id).($mgCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($pa)
+ foreach ($pa in $mgJson.($mgCap).Keys) {
+ $hlp = $mgJson.($mgCap).($pa)
if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
$displayName = 'noDisplayNameGiven'
}
@@ -68,15 +83,20 @@ function buildTree($mgId, $prnt) {
if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)")) {
$null = New-Item -Name $path -ItemType directory -Path $outputPath
}
-
$jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
+
+ $pathTracking = "$($JSONPath)$($DirectorySeparatorChar)Assignments_tracking$($DirectorySeparatorChar)$($mgCap)$($DirectorySeparatorChar)Mg$($DirectorySeparatorChar)$($mgNameValid)"
+ if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)")) {
+ $null = New-Item -Name $pathTracking -ItemType directory -Path $outputPath
+ }
+ $jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)$($DirectorySeparatorChar)$(removeInvalidFileNameChars $hlp.name).json" -Encoding utf8
}
}
#marker
if ($mgCap -eq 'RoleAssignments') {
$mgCapShort = 'ra'
- foreach ($ra in $htJSON.ManagementGroups.($getMg.Id).($mgCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($ra)
+ foreach ($ra in $mgJson.($mgCap).Keys) {
+ $hlp = $mgJson.($mgCap).($ra)
if ($hlp.PIM -eq 'true') {
$pim = 'PIM_'
}
@@ -94,15 +114,15 @@ function buildTree($mgId, $prnt) {
}
if ($mgCap -eq 'Subscriptions') {
- foreach ($sub in $htJSON.ManagementGroups.($getMg.Id).($mgCap).Keys) {
- $subNameValid = removeInvalidFileNameChars $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).SubscriptionName
+ foreach ($sub in $mgJson.($mgCap).Keys) {
+ $subNameValid = removeInvalidFileNameChars $mgJson.($mgCap).($sub).SubscriptionName
$subFolderName = "$($prntx)$($DirectorySeparatorChar)$($subNameValid) ($($sub))"
$null = New-Item -Name $subFolderName -ItemType directory -Path $outputPath
- foreach ($subCap in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).Keys) {
+ foreach ($subCap in $mgJson.($mgCap).($sub).Keys) {
if ($subCap -eq 'PolicyDefinitionsCustom') {
$subCapShort = 'pd'
- foreach ($pdc in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($pdc)
+ foreach ($pdc in $mgJson.($mgCap).($sub).($subCap).Keys) {
+ $hlp = $mgJson.($mgCap).($sub).($subCap).($pdc)
if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
$displayName = 'noDisplayNameGiven'
}
@@ -116,12 +136,19 @@ function buildTree($mgId, $prnt) {
$null = New-Item -Name $path -ItemType directory -Path $outputPath
}
$jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
+
+ $jsonConvertedTracking = $hlp | ConvertTo-Json -Depth 99
+ $pathTracking = "$($JSONPath)$($DirectorySeparatorChar)Definitions_tracking$($DirectorySeparatorChar)PolicyDefinitions$($DirectorySeparatorChar)Custom$($DirectorySeparatorChar)Sub$($DirectorySeparatorChar)$($sub)"
+ if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)")) {
+ $null = New-Item -Name $pathTracking -ItemType directory -Path $outputPath
+ }
+ $jsonConvertedTracking | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)$($DirectorySeparatorChar)$(removeInvalidFileNameChars $hlp.name).json" -Encoding utf8
}
}
if ($subCap -eq 'PolicySetDefinitionsCustom') {
$subCapShort = 'psd'
- foreach ($psdc in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($psdc)
+ foreach ($psdc in $mgJson.($mgCap).($sub).($subCap).Keys) {
+ $hlp = $mgJson.($mgCap).($sub).($subCap).($psdc)
if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
$displayName = 'noDisplayNameGiven'
}
@@ -135,12 +162,19 @@ function buildTree($mgId, $prnt) {
$null = New-Item -Name $path -ItemType directory -Path $outputPath
}
$jsonConverted | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($path)$($DirectorySeparatorChar)$($displayName) ($(removeInvalidFileNameChars $hlp.name)).json" -Encoding utf8
+
+ $jsonConvertedTracking = $hlp | ConvertTo-Json -Depth 99
+ $pathTracking = "$($JSONPath)$($DirectorySeparatorChar)Definitions_tracking$($DirectorySeparatorChar)PolicySetDefinitions$($DirectorySeparatorChar)Custom$($DirectorySeparatorChar)Sub$($DirectorySeparatorChar)$($sub)"
+ if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)")) {
+ $null = New-Item -Name $pathTracking -ItemType directory -Path $outputPath
+ }
+ $jsonConvertedTracking | Set-Content -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($pathTracking)$($DirectorySeparatorChar)$(removeInvalidFileNameChars $hlp.name).json" -Encoding utf8
}
}
if ($subCap -eq 'PolicyAssignments') {
$subCapShort = 'pa'
- foreach ($pa in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($pa)
+ foreach ($pa in $mgJson.($mgCap).($sub).($subCap).Keys) {
+ $hlp = $mgJson.($mgCap).($sub).($subCap).($pa)
if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
$displayName = 'noDisplayNameGiven'
}
@@ -159,8 +193,8 @@ function buildTree($mgId, $prnt) {
#marker
if ($subCap -eq 'RoleAssignments') {
$subCapShort = 'ra'
- foreach ($ra in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($ra)
+ foreach ($ra in $mgJson.($mgCap).($sub).($subCap).Keys) {
+ $hlp = $mgJson.($mgCap).($sub).($subCap).($ra)
if ($hlp.PIM -eq 'true') {
$pim = 'PIM_'
}
@@ -181,12 +215,12 @@ function buildTree($mgId, $prnt) {
if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsOnPolicy) {
if (-not $JsonExportExcludeResourceGroups) {
if ($subCap -eq 'ResourceGroups') {
- foreach ($rg in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys | Sort-Object) {
+ foreach ($rg in $mgJson.($mgCap).($sub).($subCap).Keys | Sort-Object) {
if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($rg)")) {
$null = New-Item -Name "$($subFolderName)$($DirectorySeparatorChar)$($rg)" -ItemType directory -Path "$($outputPath)"
}
- foreach ($pa in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).PolicyAssignments.keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).PolicyAssignments.($pa)
+ foreach ($pa in $mgJson.($mgCap).($sub).($subCap).($rg).PolicyAssignments.keys) {
+ $hlp = $mgJson.($mgCap).($sub).($subCap).($rg).PolicyAssignments.($pa)
if ([string]::IsNullOrEmpty($hlp.properties.displayName)) {
$displayName = 'noDisplayNameGiven'
}
@@ -211,12 +245,12 @@ function buildTree($mgId, $prnt) {
if (-not $azAPICallConf['htParameters'].DoNotIncludeResourceGroupsAndResourcesOnRBAC) {
if (-not $JsonExportExcludeResourceGroups) {
if ($subCap -eq 'ResourceGroups') {
- foreach ($rg in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).Keys | Sort-Object) {
+ foreach ($rg in $mgJson.($mgCap).($sub).($subCap).Keys | Sort-Object) {
if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($rg)")) {
$null = New-Item -Name "$($subFolderName)$($DirectorySeparatorChar)$($rg)" -ItemType directory -Path "$($outputPath)"
}
- foreach ($ra in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).RoleAssignments.keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).RoleAssignments.($ra)
+ foreach ($ra in $mgJson.($mgCap).($sub).($subCap).($rg).RoleAssignments.keys) {
+ $hlp = $mgJson.($mgCap).($sub).($subCap).($rg).RoleAssignments.($ra)
if ($hlp.PIM -eq 'true') {
$pim = 'PIM_'
}
@@ -234,12 +268,12 @@ function buildTree($mgId, $prnt) {
#res
if (-not $JsonExportExcludeResources) {
- foreach ($res in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).Resources.keys) {
+ foreach ($res in $mgJson.($mgCap).($sub).($subCap).($rg).Resources.keys) {
if (-not (Test-Path -LiteralPath "$($outputPath)$($DirectorySeparatorChar)$($subFolderName)$($DirectorySeparatorChar)$($rg)$($DirectorySeparatorChar)$($res)")) {
$null = New-Item -Name "$($subFolderName)$($DirectorySeparatorChar)$($rg)$($DirectorySeparatorChar)$($res)" -ItemType directory -Path "$($outputPath)"
}
- foreach ($ra in $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).Resources.($res).RoleAssignments.keys) {
- $hlp = $htJSON.ManagementGroups.($getMg.Id).($mgCap).($sub).($subCap).($rg).Resources.($res).RoleAssignments.($ra)
+ foreach ($ra in $mgJson.($mgCap).($sub).($subCap).($rg).Resources.($res).RoleAssignments.keys) {
+ $hlp = $mgJson.($mgCap).($sub).($subCap).($rg).Resources.($res).RoleAssignments.($ra)
if ($hlp.PIM -eq 'true') {
$pim = 'PIM_'
}
diff --git a/pwsh/dev/functions/cacheBuiltIn.ps1 b/pwsh/dev/functions/cacheBuiltIn.ps1
index 051626d0..26129509 100644
--- a/pwsh/dev/functions/cacheBuiltIn.ps1
+++ b/pwsh/dev/functions/cacheBuiltIn.ps1
@@ -79,12 +79,11 @@ function cacheBuiltIn {
foreach ($roledefinitionId in $builtinPolicyDefinition.properties.policyRule.then.details.roleDefinitionIds) {
if (-not $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId)) {
$script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId) = @{}
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [array]$builtinPolicyDefinition.Id
+ $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [System.Collections.ArrayList]@()
+ $null = $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies.Add($builtinPolicyDefinition.Id)
}
else {
- $usedInPolicies = $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies
- $usedInPolicies += $builtinPolicyDefinition.Id
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = $usedInPolicies
+ $null = $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies.Add($builtinPolicyDefinition.Id)
}
}
}
@@ -168,12 +167,11 @@ function cacheBuiltIn {
foreach ($roledefinitionId in $staticPolicyDefinition.properties.policyRule.then.details.roleDefinitionIds) {
if (-not $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId)) {
$script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId) = @{}
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [array]$staticPolicyDefinition.Id
+ $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [System.Collections.ArrayList]@()
+ $null = $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies.Add($staticPolicyDefinition.Id)
}
else {
- $usedInPolicies = $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies
- $usedInPolicies += $staticPolicyDefinition.Id
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = $usedInPolicies
+ $null = $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies.Add($staticPolicyDefinition.Id)
}
}
}
@@ -243,13 +241,11 @@ function cacheBuiltIn {
$currentTask = 'Caching built-in Role definitions'
Write-Host " $currentTask"
$uri = "$($azAPICallConf['azAPIEndpointUrls'].'ARM')/subscriptions/$($azAPICallConf['checkContext'].Subscription.Id)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'BuiltInRole'"
- #$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'BuiltInRole'"
}
else {
$currentTask = "Caching built-in Role definitions (Location: '$($ARMLocation)')"
Write-Host " $currentTask"
$uri = "$($azAPICallConf['azAPIEndpointUrls']."ARM$($ARMLocation)")/subscriptions/$($azAPICallConf['checkContext'].Subscription.Id)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'BuiltInRole'"
- #$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'BuiltInRole'"
}
$method = 'GET'
@@ -289,7 +285,7 @@ function cacheBuiltIn {
($script:htCacheDefinitionsRole).($roleDefinition.name).NotActions = ($roleDefinition.properties.permissions.notActions)
($script:htCacheDefinitionsRole).($roleDefinition.name).DataActions = ($roleDefinition.properties.permissions.dataActions)
($script:htCacheDefinitionsRole).($roleDefinition.name).NotDataActions = ($roleDefinition.properties.permissions.notDataActions)
- ($script:htCacheDefinitionsRole).($roleDefinition.name).Json = ($roleDefinition.properties)
+ ($script:htCacheDefinitionsRole).($roleDefinition.name).Json = $roleDefinition
($script:htCacheDefinitionsRole).($roleDefinition.name).LinkToAzAdvertizer = "$($roleDefinition.properties.roleName) "
($script:htCacheDefinitionsRole).($roleDefinition.name).RoleCanDoRoleAssignments = $roleCapable4RoleAssignmentsWrite
}
diff --git a/pwsh/dev/functions/dataCollection/dataCollectionFunctions.ps1 b/pwsh/dev/functions/dataCollection/dataCollectionFunctions.ps1
index f2f5a5fd..8350acc1 100644
--- a/pwsh/dev/functions/dataCollection/dataCollectionFunctions.ps1
+++ b/pwsh/dev/functions/dataCollection/dataCollectionFunctions.ps1
@@ -27,7 +27,7 @@ function dataCollectionDefenderPlans {
)
$currentTask = "Getting Microsoft Defender for Cloud plans for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
+ #https://learn.microsoft.com/rest/api/defenderforcloud/pricings
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Security/pricings?api-version=2018-06-01"
$method = 'GET'
$defenderPlansResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
@@ -112,7 +112,7 @@ function dataCollectionDefenderEmailContacts {
)
$currentTask = "Getting Microsoft Defender for Cloud Email contacts for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
+ #https://learn.microsoft.com/rest/api/defenderforcloud/security-contacts
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Security/securityContacts?api-version=2020-01-01-preview"
$method = 'GET'
$defenderSecurityContactsResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -listenOn 'Content' -currentTask $currentTask -caller 'CustomDataCollection'
@@ -209,7 +209,6 @@ function dataCollectionVNets {
)
$currentTask = "Getting Virtual Networks for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Network/virtualNetworks?api-version=2022-05-01"
$method = 'GET'
$networkResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
@@ -236,7 +235,6 @@ function dataCollectionPrivateEndpoints {
)
$currentTask = "Getting Private Endpoints for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$SubscriptionQuotaId']"
- #https://docs.microsoft.com/en-us/rest/api/securitycenter/pricings
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Network/privateEndpoints?api-version=2022-05-01"
$method = 'GET'
$privateEndpointsResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection' -unhandledErrorAction Continue
@@ -512,15 +510,19 @@ function dataCollectionResources {
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/resources?`$expand=createdTime,changedTime,properties&api-version=2023-07-01"
$method = 'GET'
$resourcesSubscriptionResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
- #Write-Host 'arm resList count:'$resourcesSubscriptionResult.Count
#endregion resources LIST
#region resources GET
if ($resourcesSubscriptionResult.Count -gt 0) {
$arrayResourcesWithProperties = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
- $resourcesSubscriptionResult | ForEach-Object -Parallel {
- $resource = $_
+ $batchSize = [math]::ceiling($resourcesSubscriptionResult.Count / $azAPICallConf['htParameters'].ThrottleLimit)
+ #Write-Host "Optimal batch size: $($batchSize)"
+ $counterBatch = [PSCustomObject] @{ Value = 0 }
+ $resourcesSubscriptionResultBatch = ($resourcesSubscriptionResult) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ #Write-Host "Processing data in $($resourcesSubscriptionResultBatch.Count) batches"
+
+ $resourcesSubscriptionResultBatch | ForEach-Object -Parallel {
#region using
$arrayResourcesWithProperties = $using:arrayResourcesWithProperties
$htResourceProvidersRef = $using:htResourceProvidersRef
@@ -534,65 +536,62 @@ function dataCollectionResources {
#$htResourcesWithProperties = $using:htResourcesWithProperties
#endregion using
- if ($htAvailablePrivateEndpointTypes.(($resource.type).ToLower())) {
- #Write-Host "$($resource.type) in `$htAvailablePrivateEndpointTypes"
- if ($htResourceProvidersRef.($resource.type)) {
- if ($htResourceProvidersRef.($resource.type).APIDefault) {
- $apiVersionToUse = $htResourceProvidersRef.($resource.type).APIDefault
- $apiRef = 'default'
- }
- else {
- $apiVersionToUse = $htResourceProvidersRef.($resource.type).APILatest
- $apiRef = 'latest'
- }
+ foreach ($resource in $_.Group) {
+
+ if ($htAvailablePrivateEndpointTypes.(($resource.type).ToLower())) {
+ if ($htResourceProvidersRef.($resource.type)) {
+ if ($htResourceProvidersRef.($resource.type).APIDefault) {
+ $apiVersionToUse = $htResourceProvidersRef.($resource.type).APIDefault
+ $apiRef = 'default'
+ }
+ else {
+ $apiVersionToUse = $htResourceProvidersRef.($resource.type).APILatest
+ $apiRef = 'latest'
+ }
- $currentTask = "Getting Resource Properties API-version: '$apiVersionToUse' ($apiRef); ResourceType: '$($resource.type)'; ResourceId: '$($resource.id)'"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)$($resource.id)?api-version=$apiVersionToUse"
- $method = 'GET'
- $resourceResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection' -listenOn Content -unhandledErrorAction Continue
-
- if ($resourceResult -ne 'ResourceOrResourcegroupNotFound' -and $resourceResult -ne 'convertfromJSONError') {
- $null = $script:arrayResourcesWithProperties.Add($resourceResult)
- #$script:htResourcesWithProperties.($resourceResult.id) = $resourceResult
- if ($resourceResult.properties.privateEndpointConnections.Count -gt 0) {
- foreach ($privateEndpointConnection in $resourceResult.properties.privateEndpointConnections) {
- $resourceResultIdSplit = $resourceResult.id -split '/'
- $null = $script:arrayPrivateEndPointsFromResourceProperties.Add([PSCustomObject]@{
- ResourceName = $resourceResult.name
- ResourceType = $resourceResult.type
- ResourceId = $resourceResult.id
- ResourceResourceGroup = $resourceResultIdSplit[4]
- ResourceSubscriptionId = $scopeId
- ResourceSubscriptionName = $scopeDisplayName
- ResourceMGPath = $ChildMgParentNameChainDelimited
- privateEndpointConnection = $privateEndpointConnection
- })
+ $currentTask = "Getting Resource Properties API-version: '$apiVersionToUse' ($apiRef); ResourceType: '$($resource.type)'; ResourceId: '$($resource.id)'"
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)$($resource.id)?api-version=$apiVersionToUse"
+ $method = 'GET'
+ $resourceResult = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection' -listenOn Content -unhandledErrorAction Continue
+
+ if ($resourceResult -ne 'ResourceOrResourcegroupNotFound' -and $resourceResult -ne 'convertfromJSONError') {
+ $null = $script:arrayResourcesWithProperties.Add($resourceResult)
+ #$script:htResourcesWithProperties.($resourceResult.id) = $resourceResult
+ if ($resourceResult.properties.privateEndpointConnections.Count -gt 0) {
+ foreach ($privateEndpointConnection in $resourceResult.properties.privateEndpointConnections) {
+ $resourceResultIdSplit = $resourceResult.id -split '/'
+ $null = $script:arrayPrivateEndPointsFromResourceProperties.Add([PSCustomObject]@{
+ ResourceName = $resourceResult.name
+ ResourceType = $resourceResult.type
+ ResourceId = $resourceResult.id
+ ResourceResourceGroup = $resourceResultIdSplit[4]
+ ResourceSubscriptionId = $scopeId
+ ResourceSubscriptionName = $scopeDisplayName
+ ResourceMGPath = $ChildMgParentNameChainDelimited
+ privateEndpointConnection = $privateEndpointConnection
+ })
+ }
+ }
+ }
+ else {
+ if ($resourceResult -eq 'convertfromJSONError') {
+ $script:htResourcePropertiesConvertfromJSONFailed.($resource.id) = @{}
}
}
}
else {
- if ($resourceResult -eq 'convertfromJSONError') {
- $script:htResourcePropertiesConvertfromJSONFailed.($resource.id) = @{}
- }
+ Write-Host "[Azure Governance Visualizer] Please file an issue at the Azure Governance Visualizer GitHub repository (aka.ms/AzGovViz) and provide this information (scrub subscription Id and company identifyable names): No API-version matches! ResourceType: '$($resource.type)'; ResourceId: '$($resource.id)' - Thank you!" -ForegroundColor DarkRed
}
}
else {
- Write-Host "[Azure Governance Visualizer] Please file an issue at the Azure Governance Visualizer GitHub repository (aka.ms/AzGovViz) and provide this information (scrub subscription Id and company identifyable names): No API-version matches! ResourceType: '$($resource.type)'; ResourceId: '$($resource.id)' - Thank you!" -ForegroundColor DarkRed
+ #Write-Host "$($resource.type) not in `$htAvailablePrivateEndpointTypes"
}
}
- else {
- #Write-Host "$($resource.type) not in `$htAvailablePrivateEndpointTypes"
- }
} -ThrottleLimit $azAPICallConf['htParameters'].ThrottleLimit
}
- #Write-Host 'arm resGet count:' $arrayResourcesWithProperties.Count
#endregion resources GET
- # if ($resourcesSubscriptionResult.Count -ne $arrayResourcesWithProperties.Count) {
- # Write-Host " FYI: Getting Resources for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId'] - ARM list count: $($resourcesSubscriptionResult.Count); ARG get count: $($arrayResourcesWithProperties.Count)"
- # }
-
#region PSRule
if ($azAPICallConf['htParameters'].DoPSRule -eq $true) {
if ($resourcesSubscriptionResult.Count -gt 0) {
@@ -1255,7 +1254,6 @@ function dataCollectionResources {
foreach ($naming in $namingConvention) {
if (($resource.name).StartsWith($naming, 'CurrentCultureIgnoreCase')) {
$cafResourceNamingCheck = 'passed'
- #$applicableNaming = $naming
}
}
}
@@ -1360,7 +1358,6 @@ function dataCollectionResourceGroups {
$subscriptionQuotaId
)
- #https://management.azure.com/subscriptions/{subscriptionId}/resourcegroups?api-version=2020-06-01
$currentTask = "Getting ResourceGroups for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/resourcegroups?api-version=2021-04-01"
$method = 'GET'
@@ -2195,12 +2192,11 @@ function dataCollectionPolicyDefinitions {
if (-not [string]::IsNullOrEmpty($roledefinitionId)) {
if (-not $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId)) {
$script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId) = @{}
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [array]$hlpPolicyDefinitionId
+ $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = [System.Collections.ArrayList]@()
+ $null = $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies.Add($hlpPolicyDefinitionId)
}
else {
- $usedInPolicies = $htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies
- $usedInPolicies += $hlpPolicyDefinitionId
- $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies = $usedInPolicies
+ $script:htRoleDefinitionIdsUsedInPolicy.($roledefinitionId).UsedInPolicies.Add($hlpPolicyDefinitionId)
}
}
else {
@@ -2778,7 +2774,6 @@ function dataCollectionPolicyAssignmentsMG {
$policySetCategory = $policySetDefinition.Category
}
else {
- #test
#Write-Host "pa '($L0mgmtGroupPolicyAssignment.Id)' scope: '$($scopeId)' - policySetDefinition not available: $policySetDefinitionId"
Start-Sleep -Seconds 1
}
@@ -3469,11 +3464,11 @@ function dataCollectionRoleDefinitions {
if ($TargetMgOrSub -eq 'Sub') {
$currentTask = "Getting Custom Role definitions for Subscription: '$($scopeDisplayName)' ('$scopeId') [quotaId:'$subscriptionQuotaId']"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/roleDefinitions?api-version=2018-07-01&`$filter=type eq 'CustomRole'"
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($scopeId)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'CustomRole'"
}
if ($TargetMgOrSub -eq 'MG') {
$currentTask = "Getting Custom Role definitions for Management Group: '$($scopeDisplayName)' ('$scopeId')"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($scopeId)/providers/Microsoft.Authorization/roleDefinitions?api-version=2018-07-01&`$filter=type eq 'CustomRole'"
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($scopeId)/providers/Microsoft.Authorization/roleDefinitions?api-version=2022-05-01-preview&`$filter=type eq 'CustomRole'"
}
$method = 'GET'
$scopeCustomRoleDefinitions = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -caller 'CustomDataCollection'
@@ -3559,7 +3554,6 @@ function dataCollectionRoleAssignmentsMG {
$roleAssignmentScheduleInstances = ($roleAssignmentScheduleInstancesFromAPI.where( { ($_.properties.roleAssignmentScheduleId -replace '.*/') -ne ($_.properties.originRoleAssignmentId -replace '.*/') }))
$roleAssignmentScheduleInstancesCount = $roleAssignmentScheduleInstances.Count
if ($roleAssignmentScheduleInstancesCount -gt 0) {
- #$htRoleAssignmentsPIM = @{}
foreach ($roleAssignmentScheduleInstance in $roleAssignmentScheduleInstances) {
$script:htRoleAssignmentsPIM.($roleAssignmentScheduleInstance.properties.originRoleAssignmentId.tolower()) = $roleAssignmentScheduleInstance.properties
}
@@ -3841,7 +3835,6 @@ function dataCollectionRoleAssignmentsSub {
$roleAssignmentScheduleInstances = ($roleAssignmentScheduleInstancesFromAPI.where( { ($_.properties.roleAssignmentScheduleId -replace '.*/') -ne ($_.properties.originRoleAssignmentId -replace '.*/') }))
$roleAssignmentScheduleInstancesCount = $roleAssignmentScheduleInstances.Count
if ($roleAssignmentScheduleInstancesCount -gt 0) {
- #$htRoleAssignmentsPIM = @{}
foreach ($roleAssignmentScheduleInstance in $roleAssignmentScheduleInstances) {
$script:htRoleAssignmentsPIM.($roleAssignmentScheduleInstance.properties.originRoleAssignmentId.tolower()) = $roleAssignmentScheduleInstance.properties
}
@@ -4195,4 +4188,4 @@ function dataCollectionClassicAdministratorsSub {
}
$funcDataCollectionClassicAdministratorsSub = $function:dataCollectionClassicAdministratorsSub.ToString()
-#endregion functions4DataCollection
+#endregion functions4DataCollection
\ No newline at end of file
diff --git a/pwsh/dev/functions/detectPolicyEffect.ps1 b/pwsh/dev/functions/detectPolicyEffect.ps1
index 2384226a..14fa0ad5 100644
--- a/pwsh/dev/functions/detectPolicyEffect.ps1
+++ b/pwsh/dev/functions/detectPolicyEffect.ps1
@@ -47,10 +47,10 @@ function detectPolicyEffect {
if (-not [string]::IsNullOrWhiteSpace($policyDefinition.properties.parameters.($Match.Value).allowedValues)) {
if ($policyDefinition.properties.parameters.($Match.Value).allowedValues.Count -gt 0) {
#Write-Host "allowedValues count $($policyDefinition.properties.parameters.($Match.Value).allowedValues) - $($policyDefinition.name) ($($policyDefinition.properties.policyType))"
- $arrayAllowed = @()
+ $arrayAllowed = [System.Collections.ArrayList]@()
foreach ($allowedValue in $policyDefinition.properties.parameters.($Match.Value).allowedValues) {
if ($allowedValue -in $ValidPolicyEffects) {
- $arrayAllowed += $allowedValue
+ $null = $arrayAllowed.Add($allowedValue)
}
else {
Write-Host "invalid allowedValue effect $($allowedValue) - $($policyDefinition.name) ($($policyDefinition.properties.policyType))"
diff --git a/pwsh/dev/functions/getConsumption.ps1 b/pwsh/dev/functions/getConsumption.ps1
index 95830d8e..9c502f97 100644
--- a/pwsh/dev/functions/getConsumption.ps1
+++ b/pwsh/dev/functions/getConsumption.ps1
@@ -33,7 +33,7 @@ function getConsumption {
#$subscriptionIdsOptimizedForBody = '"{0}"' -f ($subsToProcessInCustomDataCollection.subscriptionId -join '","')
$currenttask = "Getting Consumption data (scope MG '$($ManagementGroupId)') for $($subsToProcessInCustomDataCollectionCount) Subscriptions (QuotaId Whitelist: '$($SubscriptionQuotaIdWhitelist -join ', ')') for period $AzureConsumptionPeriod days ($azureConsumptionStartDate - $azureConsumptionEndDate)"
Write-Host "$currentTask"
- #https://docs.microsoft.com/en-us/rest/api/cost-management/query/usage
+ #https://learn.microsoft.com/rest/api/cost-management/query/usage
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)/providers/Microsoft.CostManagement/query?api-version=2023-03-01&`$top=5000"
$method = 'POST'
@@ -184,7 +184,7 @@ function getConsumption {
$currentTask = " Getting Consumption data (scope Sub $($subNameToProcess) '$($subIdToProcess)' ($($subscriptionQuotaIdToProcess)) (whitelist))"
#test
Write-Host $currentTask
- #https://docs.microsoft.com/en-us/rest/api/cost-management/query/usage
+ #https://learn.microsoft.com/rest/api/cost-management/query/usage
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($subIdToProcess)/providers/Microsoft.CostManagement/query?api-version=2023-03-01&`$top=5000"
$method = 'POST'
$subConsumptionData = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -currentTask $currentTask -listenOn 'ContentProperties'
@@ -245,7 +245,7 @@ function getConsumption {
#region mgScope
$currenttask = "Getting Consumption data (scope MG '$($ManagementGroupId)') for period $AzureConsumptionPeriod days ($azureConsumptionStartDate - $azureConsumptionEndDate)"
Write-Host "$currentTask"
- #https://docs.microsoft.com/en-us/rest/api/cost-management/query/usage
+ #https://learn.microsoft.com/rest/api/cost-management/query/usage
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($ManagementGroupId)/providers/Microsoft.CostManagement/query?api-version=2023-03-01&`$top=5000"
$method = 'POST'
$body = @"
@@ -376,7 +376,7 @@ function getConsumption {
$currentTask = " Getting Consumption data (scope Sub $($subNameToProcess) '$($subIdToProcess)' ($($subscriptionQuotaIdToProcess)))"
#test
Write-Host $currentTask
- #https://docs.microsoft.com/en-us/rest/api/cost-management/query/usage
+ #https://learn.microsoft.com/rest/api/cost-management/query/usage
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($subIdToProcess)/providers/Microsoft.CostManagement/query?api-version=2023-03-01&`$top=5000"
$method = 'POST'
$subConsumptionData = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -body $body -currentTask $currentTask -listenOn 'ContentProperties'
diff --git a/pwsh/dev/functions/getDefaultManagementGroup.ps1 b/pwsh/dev/functions/getDefaultManagementGroup.ps1
index 7b77701b..61193635 100644
--- a/pwsh/dev/functions/getDefaultManagementGroup.ps1
+++ b/pwsh/dev/functions/getDefaultManagementGroup.ps1
@@ -1,7 +1,7 @@
function getDefaultManagementGroup {
$currentTask = 'Get Default Management Group'
Write-Host $currentTask
- #https://docs.microsoft.com/en-us/azure/governance/management-groups/how-to/protect-resource-hierarchy#setting---default-management-group
+ #https://learn.microsoft.com/azure/governance/management-groups/how-to/protect-resource-hierarchy#setting---default-management-group
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.Management/managementGroups/$($azAPICallConf['checkContext'].Tenant.Id)/settings?api-version=2020-02-01"
$method = 'GET'
$settingsMG = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
diff --git a/pwsh/dev/functions/getMDfCSecureScoreMG.ps1 b/pwsh/dev/functions/getMDfCSecureScoreMG.ps1
index c72c382f..3942e623 100644
--- a/pwsh/dev/functions/getMDfCSecureScoreMG.ps1
+++ b/pwsh/dev/functions/getMDfCSecureScoreMG.ps1
@@ -2,7 +2,7 @@ function getMDfCSecureScoreMG {
$start = Get-Date
$currentTask = 'Getting Microsoft Defender for Cloud Secure Score for Management Groups'
Write-Host $currentTask
- #ref: https://docs.microsoft.com/en-us/azure/governance/management-groups/resource-graph-samples?tabs=azure-cli#secure-score-per-management-group
+ #ref: https://learn.microsoft.com/azure/governance/management-groups/resource-graph-samples?tabs=azure-cli#secure-score-per-management-group
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01"
$method = 'POST'
@@ -33,25 +33,22 @@ function getMDfCSecureScoreMG {
}
"@
- $getMgAscSecureScore = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -body $body -listenOn 'Content'
-
+ $getMgAscSecureScore = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask -body $body -listenOn 'Content' -unhandledErrorAction ContinueQuiet
if ($getMgAscSecureScore) {
- if ($getMgAscSecureScore -eq 'capitulation') {
- Write-Host ' Microsoft Defender for Cloud SecureScore for Management Groups will not be available' -ForegroundColor Yellow
- }
- else {
- Write-Host " Retrieved 'Microsoft Defender for Cloud' SecureScore for $($getMgAscSecureScore.Count) Management Groups"
- foreach ($entry in $getMgAscSecureScore) {
- $script:htMgASCSecureScore.($entry.mgId) = @{}
- if ($entry.secureScore -eq 404) {
- $script:htMgASCSecureScore.($entry.mgId).SecureScore = 'n/a'
- }
- else {
- $script:htMgASCSecureScore.($entry.mgId).SecureScore = $entry.secureScore
- }
+ Write-Host " Retrieved 'Microsoft Defender for Cloud' SecureScore for $($getMgAscSecureScore.Count) Management Groups"
+ foreach ($entry in $getMgAscSecureScore) {
+ $script:htMgASCSecureScore.($entry.mgId) = @{}
+ if ($entry.secureScore -eq 404) {
+ $script:htMgASCSecureScore.($entry.mgId).SecureScore = 'n/a'
+ }
+ else {
+ $script:htMgASCSecureScore.($entry.mgId).SecureScore = $entry.secureScore
}
}
}
+ else {
+ Write-Host ' Microsoft Defender for Cloud SecureScore for Management Groups will not be available' -ForegroundColor Yellow
+ }
$end = Get-Date
Write-Host "Getting Microsoft Defender for Cloud Secure Score for Management Groups duration: $((New-TimeSpan -Start $start -End $end).TotalMinutes) minutes ($((New-TimeSpan -Start $start -End $end).TotalSeconds) seconds)"
diff --git a/pwsh/dev/functions/getOrphanedResources.ps1 b/pwsh/dev/functions/getOrphanedResources.ps1
index 346aac3f..cdf2a3cf 100644
--- a/pwsh/dev/functions/getOrphanedResources.ps1
+++ b/pwsh/dev/functions/getOrphanedResources.ps1
@@ -86,7 +86,7 @@ function getOrphanedResources {
$subsToProcessInCustomDataCollection = $using:subsToProcessInCustomDataCollection
$azAPICallConf = $using:azAPICallConf
- #Batching: https://docs.microsoft.com/en-us/azure/governance/resource-graph/troubleshoot/general#toomanysubscription
+ #Batching: https://learn.microsoft.com/azure/governance/resource-graph/troubleshoot/general#toomanysubscription
$counterBatch = [PSCustomObject] @{ Value = 0 }
$batchSize = 1000
$subscriptionsBatch = $subsToProcessInCustomDataCollection | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
diff --git a/pwsh/dev/functions/getPIMEligible.ps1 b/pwsh/dev/functions/getPIMEligible.ps1
index 16cfb5e8..d97185bb 100644
--- a/pwsh/dev/functions/getPIMEligible.ps1
+++ b/pwsh/dev/functions/getPIMEligible.ps1
@@ -53,143 +53,156 @@ function getPIMEligible {
$htPIMEligibleDirect = [System.Collections.Hashtable]::Synchronized((New-Object System.Collections.Hashtable)) #@{}
$relevantSubscriptionIds = $subsToProcessInCustomDataCollection.subscriptionId
- $scopesToIterate | ForEach-Object -Parallel {
- $scope = $_
- $azAPICallConf = $using:azAPICallConf
- $arrayPIMEligible = $using:arrayPIMEligible
- $htPIMEligibleDirect = $using:htPIMEligibleDirect
- if ($scope.type -eq 'managementgroup') { $htManagementGroupsMgPath = $using:htManagementGroupsMgPath }
- if ($scope.type -eq 'subscription') { $htSubscriptionsMgPath = $using:htSubscriptionsMgPath }
- $htPrincipals = $using:htPrincipals
- $htUserTypesGuest = $using:htUserTypesGuest
- $htServicePrincipals = $using:htServicePrincipals
- $relevantSubscriptionIds = $using:relevantSubscriptionIds
- $function:resolveObjectIds = $using:funcResolveObjectIds
- $function:testGuid = $using:funcTestGuid
-
- $processThisScope = $true
- if ($scope.type -eq 'subscription') {
- if (($scope.externalId -replace '.*/') -notin $relevantSubscriptionIds) {
- Write-Host " Non relevant subscriptionId '$(($scope.externalId -replace '.*/'))' /skipping this subscription as it is not contained in the 'Relevant Subscriptions' collection (needs investigation)" -ForegroundColor DarkRed
- $processThisScope = $false
- }
- }
- if ($processThisScope -eq $true) {
- $currentTask = "Get Eligible assignments for Scope $($scope.type): $($scope.externalId -replace '.*/')"
- $extUri = "?`$expand=linkedEligibleRoleAssignment,subject,roleDefinition(`$expand=resource)&`$count=true&`$filter=(roleDefinition/resource/id eq '$($scope.id)')+and+(assignmentState eq 'Eligible')&`$top=100"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/privilegedAccess/azureResources/roleAssignments" + $extUri
- $resx = AzAPICall -AzAPICallConfiguration $azapicallConf -currentTask $currentTask -uri $uri
+ if ($scopesToIterate.Count -gt 0) {
+
+ $batchSize = [math]::ceiling($scopesToIterate.Count / $ThrottleLimit)
+ Write-Host "Optimal batch size: $($batchSize)"
+ $counterBatch = [PSCustomObject] @{ Value = 0 }
+ $scopesToIterateBatch = ($scopesToIterate) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ Write-Host "Processing data in $($scopesToIterateBatch.Count) batches"
+
+ $scopesToIterateBatch | ForEach-Object -Parallel {
+ $scope = $_
+ $azAPICallConf = $using:azAPICallConf
+ $arrayPIMEligible = $using:arrayPIMEligible
+ $htPIMEligibleDirect = $using:htPIMEligibleDirect
+ $htPrincipals = $using:htPrincipals
+ $htUserTypesGuest = $using:htUserTypesGuest
+ $htServicePrincipals = $using:htServicePrincipals
+ $relevantSubscriptionIds = $using:relevantSubscriptionIds
+ $function:resolveObjectIds = $using:funcResolveObjectIds
+ $function:testGuid = $using:funcTestGuid
+
+ foreach ($scope in $_.Group) {
+ if ($scope.type -eq 'managementgroup') { $htManagementGroupsMgPath = $using:htManagementGroupsMgPath }
+ if ($scope.type -eq 'subscription') { $htSubscriptionsMgPath = $using:htSubscriptionsMgPath }
+
+ $processThisScope = $true
+ if ($scope.type -eq 'subscription') {
+ if (($scope.externalId -replace '.*/') -notin $relevantSubscriptionIds) {
+ Write-Host " Non relevant subscriptionId '$(($scope.externalId -replace '.*/'))' /skipping this subscription as it is not contained in the 'Relevant Subscriptions' collection (needs investigation)" -ForegroundColor DarkRed
+ $processThisScope = $false
+ }
+ }
- if ($resx.Count -gt 0) {
+ if ($processThisScope -eq $true) {
+ $currentTask = "Get Eligible assignments for Scope $($scope.type): $($scope.externalId -replace '.*/')"
+ $extUri = "?`$expand=linkedEligibleRoleAssignment,subject,roleDefinition(`$expand=resource)&`$count=true&`$filter=(roleDefinition/resource/id eq '$($scope.id)')+and+(assignmentState eq 'Eligible')&`$top=100"
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/privilegedAccess/azureResources/roleAssignments" + $extUri
+ $resx = AzAPICall -AzAPICallConfiguration $azapicallConf -currentTask $currentTask -uri $uri
- $users = $resx.where({ $_.subject.type -eq 'user' })
- if ($users.Count -gt 0) {
- ResolveObjectIds -objectIds $users.subject.id -showActivity
- }
+ if ($resx.Count -gt 0) {
- foreach ($entry in $resx) {
- $scopeId = $scope.externalId -replace '.*/'
- if ($scope.type -eq 'managementgroup') {
- $ScopeType = 'MG'
- $ManagementGroupId = $scopeId
- $SubscriptionId = ''
- $SubscriptionDisplayName = ''
- if ($htManagementGroupsMgPath.($scopeId)) {
- $MgDetails = $htManagementGroupsMgPath.($scopeId)
- $ManagementGroupDisplayName = $MgDetails.DisplayName
- $ScopeDisplayName = $MgDetails.DisplayName
- $MgPath = $MgDetails.path
- $MgLevel = $MgDetails.level
- }
- else {
- $ManagementGroupDisplayName = 'notAccessible'
- $ScopeDisplayName = 'notAccessible'
- $MgPath = 'notAccessible'
- $MgLevel = 'notAccessible'
+ $users = $resx.where({ $_.subject.type -eq 'user' })
+ if ($users.Count -gt 0) {
+ ResolveObjectIds -objectIds $users.subject.id -showActivity
}
- if ($entry.memberType -eq 'direct') {
- $script:htPIMEligibleDirect.($entry.id) = @{}
- $script:htPIMEligibleDirect.($entry.id).clear = $scopeId
- if ($scopeId -eq $ManagementGroupDisplayName) {
- $script:htPIMEligibleDirect.($entry.id).enriched = "$($scopeId) [Level $($MgLevel)]"
+ foreach ($entry in $resx) {
+ $scopeId = $scope.externalId -replace '.*/'
+ if ($scope.type -eq 'managementgroup') {
+ $ScopeType = 'MG'
+ $ManagementGroupId = $scopeId
+ $SubscriptionId = ''
+ $SubscriptionDisplayName = ''
+ if ($htManagementGroupsMgPath.($scopeId)) {
+ $MgDetails = $htManagementGroupsMgPath.($scopeId)
+ $ManagementGroupDisplayName = $MgDetails.DisplayName
+ $ScopeDisplayName = $MgDetails.DisplayName
+ $MgPath = $MgDetails.path
+ $MgLevel = $MgDetails.level
+ }
+ else {
+ $ManagementGroupDisplayName = 'notAccessible'
+ $ScopeDisplayName = 'notAccessible'
+ $MgPath = 'notAccessible'
+ $MgLevel = 'notAccessible'
+ }
+
+ if ($entry.memberType -eq 'direct') {
+ $script:htPIMEligibleDirect.($entry.id) = @{}
+ $script:htPIMEligibleDirect.($entry.id).clear = $scopeId
+ if ($scopeId -eq $ManagementGroupDisplayName) {
+ $script:htPIMEligibleDirect.($entry.id).enriched = "$($scopeId) [Level $($MgLevel)]"
+ }
+ else {
+ $script:htPIMEligibleDirect.($entry.id).enriched = "$($ManagementGroupDisplayName) ($($scopeId)) [Level $($MgLevel)]"
+ }
+ }
}
- else {
- $script:htPIMEligibleDirect.($entry.id).enriched = "$($ManagementGroupDisplayName) ($($scopeId)) [Level $($MgLevel)]"
+ if ($scope.type -eq 'subscription') {
+ $ScopeType = 'Sub'
+ #$ManagementGroupId = ''
+ $SubscriptionId = $scopeId
+ if ($htSubscriptionsMgPath.($scopeId)) {
+ $MgDetails = $htSubscriptionsMgPath.($scopeId)
+ $SubscriptionDisplayName = $MgDetails.DisplayName
+ $ScopeDisplayName = $MgDetails.DisplayName
+ $MgPath = $MgDetails.path
+ $MgLevel = $MgDetails.level
+ $ManagementGroupId = $MgDetails.Parent
+ $ManagementGroupDisplayName = $MgDetails.ParentName
+ }
+ else {
+ $SubscriptionDisplayName = 'notAccessible'
+ $ScopeDisplayName = 'notAccessible'
+ $MgPath = 'notAccessible'
+ $MgLevel = 'notAccessible'
+ }
+ #$ManagementGroupDisplayName = ''
+
}
- }
- }
- if ($scope.type -eq 'subscription') {
- $ScopeType = 'Sub'
- #$ManagementGroupId = ''
- $SubscriptionId = $scopeId
- if ($htSubscriptionsMgPath.($scopeId)) {
- $MgDetails = $htSubscriptionsMgPath.($scopeId)
- $SubscriptionDisplayName = $MgDetails.DisplayName
- $ScopeDisplayName = $MgDetails.DisplayName
- $MgPath = $MgDetails.path
- $MgLevel = $MgDetails.level
- $ManagementGroupId = $MgDetails.Parent
- $ManagementGroupDisplayName = $MgDetails.ParentName
- }
- else {
- $SubscriptionDisplayName = 'notAccessible'
- $ScopeDisplayName = 'notAccessible'
- $MgPath = 'notAccessible'
- $MgLevel = 'notAccessible'
- }
- #$ManagementGroupDisplayName = ''
- }
+ if ($entry.subject.type -eq 'user') {
+ if ($htPrincipals.($entry.subject.id)) {
+ $userDetail = $htPrincipals.($entry.subject.id)
+ $principalType = "$($userDetail.type) $($userDetail.userType)"
+ }
+ else {
+ $principalType = $entry.subject.type
+ }
+ }
+ else {
+ $principalType = $entry.subject.type
+ }
- if ($entry.subject.type -eq 'user') {
- if ($htPrincipals.($entry.subject.id)) {
- $userDetail = $htPrincipals.($entry.subject.id)
- $principalType = "$($userDetail.type) $($userDetail.userType)"
+ $roleType = 'undefined'
+ if ($entry.roleDefinition.type -eq 'BuiltInRole') { $roleType = 'Builtin' }
+ if ($entry.roleDefinition.type -eq 'CustomRole') { $roleType = 'Custom' }
+
+ $null = $script:arrayPIMEligible.Add([PSCustomObject]@{
+ ScopeType = $ScopeType
+ ScopeId = $scopeId
+ ScopeDisplayName = $ScopeDisplayName
+ ManagementGroupId = $ManagementGroupId
+ ManagementGroupDisplayName = $ManagementGroupDisplayName
+ SubscriptionId = $SubscriptionId
+ SubscriptionDisplayName = $SubscriptionDisplayName
+ MgPath = $MgPath
+ MgLevel = $MgLevel
+ RoleId = $entry.roleDefinition.externalId
+ RoleIdGuid = $entry.roleDefinition.externalId -replace '.*/'
+ RoleType = $roleType
+ RoleName = $entry.roleDefinition.displayName
+ IdentityObjectId = $entry.subject.id
+ IdentityType = $principalType
+ IdentityDisplayName = $entry.subject.displayName
+ IdentityPrincipalName = $entry.subject.principalName
+ PIMId = $entry.id
+ PIMInheritance = $entry.memberType
+ PIMInheritedFromClear = ''
+ PIMInheritedFrom = ''
+ PIMStartDateTime = $entry.startDateTime
+ PIMEndDateTime = $entry.endDateTime
+ })
}
- else {
- $principalType = $entry.subject.type
- }
- }
- else {
- $principalType = $entry.subject.type
}
-
- $roleType = 'undefined'
- if ($entry.roleDefinition.type -eq 'BuiltInRole') { $roleType = 'Builtin' }
- if ($entry.roleDefinition.type -eq 'CustomRole') { $roleType = 'Custom' }
-
- $null = $script:arrayPIMEligible.Add([PSCustomObject]@{
- ScopeType = $ScopeType
- ScopeId = $scopeId
- ScopeDisplayName = $ScopeDisplayName
- ManagementGroupId = $ManagementGroupId
- ManagementGroupDisplayName = $ManagementGroupDisplayName
- SubscriptionId = $SubscriptionId
- SubscriptionDisplayName = $SubscriptionDisplayName
- MgPath = $MgPath
- MgLevel = $MgLevel
- RoleId = $entry.roleDefinition.externalId
- RoleIdGuid = $entry.roleDefinition.externalId -replace '.*/'
- RoleType = $roleType
- RoleName = $entry.roleDefinition.displayName
- IdentityObjectId = $entry.subject.id
- IdentityType = $principalType
- IdentityDisplayName = $entry.subject.displayName
- IdentityPrincipalName = $entry.subject.principalName
- PIMId = $entry.id
- PIMInheritance = $entry.memberType
- PIMInheritedFromClear = ''
- PIMInheritedFrom = ''
- PIMStartDateTime = $entry.startDateTime
- PIMEndDateTime = $entry.endDateTime
- })
}
}
- }
- } -ThrottleLimit $ThrottleLimit
+ } -ThrottleLimit $ThrottleLimit
+ }
foreach ($entry in $arrayPIMEligible) {
if ($entry.PIMInheritance -eq 'inherited') {
diff --git a/pwsh/dev/functions/getPolicyRemediation.ps1 b/pwsh/dev/functions/getPolicyRemediation.ps1
index ebad7d98..d9bbc13a 100644
--- a/pwsh/dev/functions/getPolicyRemediation.ps1
+++ b/pwsh/dev/functions/getPolicyRemediation.ps1
@@ -1,7 +1,7 @@
function getPolicyRemediation {
$currentTask = 'Getting NonCompliant (dine/modify)'
Write-Host $currentTask
- #ref: https://learn.microsoft.com/en-us/rest/api/azureresourcegraph/resourcegraph(2021-03-01)/resources/resources
+ #ref: https://learn.microsoft.com/rest/api/azureresourcegraph/resourcegraph(2021-03-01)/resources/resources
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01"
$method = 'POST'
diff --git a/pwsh/dev/functions/getResourceDiagnosticsCapability.ps1 b/pwsh/dev/functions/getResourceDiagnosticsCapability.ps1
index c25b3d8b..727ffc0b 100644
--- a/pwsh/dev/functions/getResourceDiagnosticsCapability.ps1
+++ b/pwsh/dev/functions/getResourceDiagnosticsCapability.ps1
@@ -42,7 +42,7 @@ function getResourceDiagnosticsCapability {
#thx @Jim Britt (Microsoft) https://github.com/JimGBritt/AzurePolicy/tree/master/AzureMonitor/Scripts Create-AzDiagPolicy.ps1
$responseJSON = ''
- $logCategories = @()
+ $logCategories = [System.Collections.ArrayList]@()
$metrics = $false
$logs = $false
@@ -102,7 +102,7 @@ function getResourceDiagnosticsCapability {
}
if ($response.properties.categoryType -eq 'Logs') {
$logs = $true
- $logCategories += $response.name
+ $null = $logCategories.Add($response.name)
}
}
}
diff --git a/pwsh/dev/functions/html/htmlFunctions.ps1 b/pwsh/dev/functions/html/htmlFunctions.ps1
index 8893e8a0..b8fef36c 100644
--- a/pwsh/dev/functions/html/htmlFunctions.ps1
+++ b/pwsh/dev/functions/html/htmlFunctions.ps1
@@ -238,7 +238,7 @@ function processScopeInsights($mgChild, $mgChildOf) {
"@
if ($mgId -eq $defaultManagementGroupId) {
$script:html += @'
- Default Management Group docs
+ Default Management Group learn
'@
}
$script:html += @"
diff --git a/pwsh/dev/functions/namingValidation.ps1 b/pwsh/dev/functions/namingValidation.ps1
index 752b1da4..7c39c788 100644
--- a/pwsh/dev/functions/namingValidation.ps1
+++ b/pwsh/dev/functions/namingValidation.ps1
@@ -1,16 +1,16 @@
function NamingValidation($toCheck) {
$checks = @(':', '/', '\', '<', '>', '|', '"')
- $array = @()
+ $array = [System.Collections.ArrayList]@()
foreach ($check in $checks) {
if ($toCheck -like "*$($check)*") {
- $array += $check
+ $null = $array.Add($check)
}
}
if ($toCheck -match '\*') {
- $array += '*'
+ $null = $array.Add('*')
}
if ($toCheck -match '\?') {
- $array += '?'
+ $null = $array.Add('?')
}
return $array
}
\ No newline at end of file
diff --git a/pwsh/dev/functions/processAADGroups.ps1 b/pwsh/dev/functions/processAADGroups.ps1
index 56e9247b..c3d209ae 100644
--- a/pwsh/dev/functions/processAADGroups.ps1
+++ b/pwsh/dev/functions/processAADGroups.ps1
@@ -1,12 +1,12 @@
function processAADGroups {
if ($NoPIMEligibility) {
- Write-Host 'Resolving AAD Groups (for which a RBAC Role assignment exists)'
+ Write-Host 'Resolving Microsoft Entra groups (for which a RBAC role assignment exists)'
}
else {
- Write-Host 'Resolving AAD Groups (for which a RBAC Role assignment or PIM Eligibility exists)'
+ Write-Host 'Resolving Microsoft Entra groups (for which a RBAC role assignment or PIM eligibility exists)'
}
- Write-Host " Users known as Guest count: $($htUserTypesGuest.Keys.Count) (before Resolving AAD Groups)"
+ Write-Host " Users known as Guest count: $($htUserTypesGuest.Keys.Count) (before resolving Microsoft Entra groups)"
$startAADGroupsResolveMembers = Get-Date
$roleAssignmentsforGroups = ($roleAssignmentsUniqueById.where( { $_.RoleAssignmentIdentityObjectType -eq 'Group' } ) | Select-Object -Property RoleAssignmentIdentityObjectId, RoleAssignmentIdentityDisplayname) | Sort-Object -Property RoleAssignmentIdentityObjectId -Unique
@@ -34,9 +34,9 @@ function processAADGroups {
})
}
}
- Write-Host " $cntPIMEligibleGroupsTotal Groups from PIM Eligibility; $cntPIMEligibleGroupsNotCoveredFromRoleAssignments Groups added ($($cntPIMEligibleGroupsTotal - $cntPIMEligibleGroupsNotCoveredFromRoleAssignments) already covered in RoleAssignments)"
+ Write-Host " $cntPIMEligibleGroupsTotal groups from PIM eligibility; $cntPIMEligibleGroupsNotCoveredFromRoleAssignments groups added ($($cntPIMEligibleGroupsTotal - $cntPIMEligibleGroupsNotCoveredFromRoleAssignments) already covered in role assignments)"
$aadGroupsCount = ($optimizedTableForAADGroupsQuery).Count
- Write-Host " $aadGroupsCount Groups from RoleAssignments and PIM Eligibility"
+ Write-Host " $aadGroupsCount groups from role assignments and PIM eligibility"
}
if ($aadGroupsCount -gt 0) {
@@ -52,10 +52,17 @@ function processAADGroups {
{ $_ -gt 10000 } { $indicator = 250 }
}
- Write-Host " processing $($aadGroupsCount) AAD Groups (indicating progress in steps of $indicator)"
+ Write-Host " processing $($aadGroupsCount) Microsoft Entra groups (indicating progress in steps of $indicator)"
- $optimizedTableForAADGroupsQuery | ForEach-Object -Parallel {
- $aadGroupIdWithRoleAssignment = $_
+ $ThrottleLimitThis = $ThrottleLimit * 2
+ $batchSize = [math]::ceiling($optimizedTableForAADGroupsQuery.Count / $ThrottleLimitThis)
+ Write-Host "Optimal batch size: $($batchSize)"
+ $counterBatch = [PSCustomObject] @{ Value = 0 }
+ $optimizedTableForAADGroupsQueryBatch = ($optimizedTableForAADGroupsQuery) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ Write-Host "Processing data in $($optimizedTableForAADGroupsQueryBatch.Count) batches"
+
+ $optimizedTableForAADGroupsQueryBatch | ForEach-Object -Parallel {
+ #$aadGroupIdWithRoleAssignment = $_
#region UsingVARs
#fromOtherFunctions
$AADGroupMembersLimit = $using:AADGroupMembersLimit
@@ -74,44 +81,44 @@ function processAADGroups {
$function:getGroupmembers = $using:funcGetGroupmembers
#endregion UsingVARs
- $rndom = Get-Random -Minimum 10 -Maximum 750
- Start-Sleep -Millisecond $rndom
+ foreach ($aadGroupIdWithRoleAssignment in $_.Group) {
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/groups/$($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)/transitiveMembers/`$count"
- $method = 'GET'
- $aadGroupMembersCount = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask "getGroupMembersCountTransitive $($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)" -listenOn 'Content' -consistencyLevel 'eventual'
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/beta/groups/$($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)/transitiveMembers/`$count"
+ $method = 'GET'
+ $aadGroupMembersCount = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask "getGroupMembersCountTransitive $($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)" -listenOn 'Content' -consistencyLevel 'eventual'
- if ($aadGroupMembersCount -eq 'Request_ResourceNotFound') {
- $null = $script:arrayGroupRequestResourceNotFound.Add([PSCustomObject]@{
- groupId = $aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId
- })
- }
- else {
- if ($aadGroupMembersCount -gt $AADGroupMembersLimit) {
- Write-Host " Group exceeding limit ($($AADGroupMembersLimit)); memberCount: $aadGroupMembersCount; Group: $($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityDisplayname) ($($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)); Members will not be resolved adjust the limit using parameter -AADGroupMembersLimit"
- $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId) = @{}
- $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersAllCount = $aadGroupMembersCount
- $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersUsersCount = 'n/a'
- $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersGroupsCount = 'n/a'
- $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersServicePrincipalsCount = 'n/a'
+ if ($aadGroupMembersCount -eq 'Request_ResourceNotFound') {
+ $null = $script:arrayGroupRequestResourceNotFound.Add([PSCustomObject]@{
+ groupId = $aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId
+ })
}
else {
- getGroupmembers -aadGroupId $aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId -aadGroupDisplayName $aadGroupIdWithRoleAssignment.RoleAssignmentIdentityDisplayname
+ if ($aadGroupMembersCount -gt $AADGroupMembersLimit) {
+ Write-Host " Group exceeding limit ($($AADGroupMembersLimit)); memberCount: $aadGroupMembersCount; Group: $($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityDisplayname) ($($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)); Members will not be resolved adjust the limit using parameter -AADGroupMembersLimit"
+ $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId) = @{}
+ $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersAllCount = $aadGroupMembersCount
+ $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersUsersCount = 'n/a'
+ $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersGroupsCount = 'n/a'
+ $script:htAADGroupsDetails.($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId).MembersServicePrincipalsCount = 'n/a'
+ }
+ else {
+ getGroupmembers -aadGroupId $aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId -aadGroupDisplayName $aadGroupIdWithRoleAssignment.RoleAssignmentIdentityDisplayname
+ }
}
- }
- $null = $script:arrayProgressedAADGroups.Add($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)
- $processedAADGroupsCount = $null
- $processedAADGroupsCount = ($arrayProgressedAADGroups).Count
- if ($processedAADGroupsCount) {
- if ($processedAADGroupsCount % $indicator -eq 0) {
- Write-Host " $processedAADGroupsCount AAD Groups processed"
+ $null = $script:arrayProgressedAADGroups.Add($aadGroupIdWithRoleAssignment.RoleAssignmentIdentityObjectId)
+ $processedAADGroupsCount = $null
+ $processedAADGroupsCount = ($arrayProgressedAADGroups).Count
+ if ($processedAADGroupsCount) {
+ if ($processedAADGroupsCount % $indicator -eq 0) {
+ Write-Host " $processedAADGroupsCount Microsoft Entra groups processed"
+ }
}
}
- } -ThrottleLimit ($ThrottleLimit * 2)
+ } -ThrottleLimit ($ThrottleLimitThis)
}
else {
- Write-Host " processing $($aadGroupsCount) AAD Groups"
+ Write-Host " processing $($aadGroupsCount) Microsoft Entra groups"
}
$arrayGroupRequestResourceNotFoundCount = ($arrayGroupRequestResourceNotFound).Count
@@ -119,8 +126,8 @@ function processAADGroups {
Write-Host "$arrayGroupRequestResourceNotFoundCount Groups could not be checked for Memberships"
}
- Write-Host " processed $($arrayProgressedAADGroups.Count) AAD Groups"
+ Write-Host " processed $($arrayProgressedAADGroups.Count) Microsoft Entra groups"
$endAADGroupsResolveMembers = Get-Date
- Write-Host "Resolving AAD Groups duration: $((New-TimeSpan -Start $startAADGroupsResolveMembers -End $endAADGroupsResolveMembers).TotalMinutes) minutes ($((New-TimeSpan -Start $startAADGroupsResolveMembers -End $endAADGroupsResolveMembers).TotalSeconds) seconds)"
- Write-Host " Users known as Guest count: $($htUserTypesGuest.Keys.Count) (after Resolving AAD Groups)"
+ Write-Host "Resolving Microsoft Entra groups duration: $((New-TimeSpan -Start $startAADGroupsResolveMembers -End $endAADGroupsResolveMembers).TotalMinutes) minutes ($((New-TimeSpan -Start $startAADGroupsResolveMembers -End $endAADGroupsResolveMembers).TotalSeconds) seconds)"
+ Write-Host " Users known as Guest count: $($htUserTypesGuest.Keys.Count) (after resolving Microsoft Entra groups)"
}
\ No newline at end of file
diff --git a/pwsh/dev/functions/processApplications.ps1 b/pwsh/dev/functions/processApplications.ps1
index 015ff84d..dcad6650 100644
--- a/pwsh/dev/functions/processApplications.ps1
+++ b/pwsh/dev/functions/processApplications.ps1
@@ -17,7 +17,15 @@ function processApplications {
$startSPApp = Get-Date
$currentDateUTC = (Get-Date).ToUniversalTime()
$script:arrayApplicationRequestResourceNotFound = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
- $servicePrincipalsOfTypeApplication | ForEach-Object -Parallel {
+
+ $ThrottleLimitThis = $ThrottleLimit * 2
+ $batchSize = [math]::ceiling($servicePrincipalsOfTypeApplication.Count / $ThrottleLimitThis)
+ Write-Host "Optimal batch size: $($batchSize)"
+ $counterBatch = [PSCustomObject] @{ Value = 0 }
+ $servicePrincipalsOfTypeApplicationBatch = ($servicePrincipalsOfTypeApplication) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ Write-Host "Processing data in $($servicePrincipalsOfTypeApplicationBatch.Count) batches"
+
+ $servicePrincipalsOfTypeApplicationBatch | ForEach-Object -Parallel {
#region UsingVARs
$currentDateUTC = $using:currentDateUTC
@@ -30,91 +38,92 @@ function processApplications {
$htServicePrincipals = $using:htServicePrincipals
#endregion UsingVARs
- $sp = $htServicePrincipals.($_)
+ foreach ($entry in $_.Group) {
+ $sp = $htServicePrincipals.($entry)
- $currentTask = "getApp $($sp.appId)"
- $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications?`$filter=appId eq '$($sp.appId)'"
- $method = 'GET'
- $getApplication = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
+ $currentTask = "getApp $($sp.appId)"
+ $uri = "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications?`$filter=appId eq '$($sp.appId)'"
+ $method = 'GET'
+ $getApplication = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method $method -currentTask $currentTask
- if ($getApplication -eq 'Request_ResourceNotFound') {
- $null = $script:arrayApplicationRequestResourceNotFound.Add([PSCustomObject]@{
- appId = $sp.appId
- })
- }
- else {
- if (($getApplication).Count -eq 0) {
- Write-Host "$($sp.appId) no data returned / seems non existent?"
+ if ($getApplication -eq 'Request_ResourceNotFound') {
+ $null = $script:arrayApplicationRequestResourceNotFound.Add([PSCustomObject]@{
+ appId = $sp.appId
+ })
}
else {
- $script:htAppDetails.($sp.id) = @{}
- $script:htAppDetails.($sp.id).servicePrincipalType = $sp.servicePrincipalType
- $script:htAppDetails.($sp.id).spGraphDetails = $sp
- $script:htAppDetails.($sp.id).appGraphDetails = $getApplication
+ if (($getApplication).Count -eq 0) {
+ Write-Host "$($sp.appId) no data returned / seems non existent?"
+ }
+ else {
+ $script:htAppDetails.($sp.id) = @{}
+ $script:htAppDetails.($sp.id).servicePrincipalType = $sp.servicePrincipalType
+ $script:htAppDetails.($sp.id).spGraphDetails = $sp
+ $script:htAppDetails.($sp.id).appGraphDetails = $getApplication
- $appPasswordCredentialsCount = ($getApplication.passwordCredentials).count
- if ($appPasswordCredentialsCount -gt 0) {
- $script:htAppDetails.($sp.id).appPasswordCredentialsCount = $appPasswordCredentialsCount
- $appPasswordCredentialsExpiredCount = 0
- $appPasswordCredentialsGracePeriodExpiryCount = 0
- $appPasswordCredentialsExpiryOKCount = 0
- $appPasswordCredentialsExpiryOKMoreThan2YearsCount = 0
- foreach ($appPasswordCredential in $getApplication.passwordCredentials) {
- $passwordExpiryTotalDays = (New-TimeSpan -Start $currentDateUTC -End $appPasswordCredential.endDateTime).TotalDays
- if ($passwordExpiryTotalDays -lt 0) {
- $appPasswordCredentialsExpiredCount++
- }
- elseif ($passwordExpiryTotalDays -lt $AADServicePrincipalExpiryWarningDays) {
- $appPasswordCredentialsGracePeriodExpiryCount++
- }
- else {
- if ($passwordExpiryTotalDays -gt 730) {
- $appPasswordCredentialsExpiryOKMoreThan2YearsCount++
+ $appPasswordCredentialsCount = ($getApplication.passwordCredentials).count
+ if ($appPasswordCredentialsCount -gt 0) {
+ $script:htAppDetails.($sp.id).appPasswordCredentialsCount = $appPasswordCredentialsCount
+ $appPasswordCredentialsExpiredCount = 0
+ $appPasswordCredentialsGracePeriodExpiryCount = 0
+ $appPasswordCredentialsExpiryOKCount = 0
+ $appPasswordCredentialsExpiryOKMoreThan2YearsCount = 0
+ foreach ($appPasswordCredential in $getApplication.passwordCredentials) {
+ $passwordExpiryTotalDays = (New-TimeSpan -Start $currentDateUTC -End $appPasswordCredential.endDateTime).TotalDays
+ if ($passwordExpiryTotalDays -lt 0) {
+ $appPasswordCredentialsExpiredCount++
+ }
+ elseif ($passwordExpiryTotalDays -lt $AADServicePrincipalExpiryWarningDays) {
+ $appPasswordCredentialsGracePeriodExpiryCount++
}
else {
- $appPasswordCredentialsExpiryOKCount++
+ if ($passwordExpiryTotalDays -gt 730) {
+ $appPasswordCredentialsExpiryOKMoreThan2YearsCount++
+ }
+ else {
+ $appPasswordCredentialsExpiryOKCount++
+ }
}
}
+ $script:htAppDetails.($sp.id).appPasswordCredentialsExpiredCount = $appPasswordCredentialsExpiredCount
+ $script:htAppDetails.($sp.id).appPasswordCredentialsGracePeriodExpiryCount = $appPasswordCredentialsGracePeriodExpiryCount
+ $script:htAppDetails.($sp.id).appPasswordCredentialsExpiryOKCount = $appPasswordCredentialsExpiryOKCount
+ $script:htAppDetails.($sp.id).appPasswordCredentialsExpiryOKMoreThan2YearsCount = $appPasswordCredentialsExpiryOKMoreThan2YearsCount
}
- $script:htAppDetails.($sp.id).appPasswordCredentialsExpiredCount = $appPasswordCredentialsExpiredCount
- $script:htAppDetails.($sp.id).appPasswordCredentialsGracePeriodExpiryCount = $appPasswordCredentialsGracePeriodExpiryCount
- $script:htAppDetails.($sp.id).appPasswordCredentialsExpiryOKCount = $appPasswordCredentialsExpiryOKCount
- $script:htAppDetails.($sp.id).appPasswordCredentialsExpiryOKMoreThan2YearsCount = $appPasswordCredentialsExpiryOKMoreThan2YearsCount
- }
- $appKeyCredentialsCount = ($getApplication.keyCredentials).count
- if ($appKeyCredentialsCount -gt 0) {
- $script:htAppDetails.($sp.id).appKeyCredentialsCount = $appKeyCredentialsCount
- $appKeyCredentialsExpiredCount = 0
- $appKeyCredentialsGracePeriodExpiryCount = 0
- $appKeyCredentialsExpiryOKCount = 0
- $appKeyCredentialsExpiryOKMoreThan2YearsCount = 0
- foreach ($appKeyCredential in $getApplication.keyCredentials) {
- $keyCredentialExpiryTotalDays = (New-TimeSpan -Start $currentDateUTC -End $appKeyCredential.endDateTime).TotalDays
- if ($keyCredentialExpiryTotalDays -lt 0) {
- $appKeyCredentialsExpiredCount++
- }
- elseif ($keyCredentialExpiryTotalDays -lt $AADServicePrincipalExpiryWarningDays) {
- $appKeyCredentialsGracePeriodExpiryCount++
- }
- else {
- if ($keyCredentialExpiryTotalDays -gt 730) {
- $appKeyCredentialsExpiryOKMoreThan2YearsCount++
+ $appKeyCredentialsCount = ($getApplication.keyCredentials).count
+ if ($appKeyCredentialsCount -gt 0) {
+ $script:htAppDetails.($sp.id).appKeyCredentialsCount = $appKeyCredentialsCount
+ $appKeyCredentialsExpiredCount = 0
+ $appKeyCredentialsGracePeriodExpiryCount = 0
+ $appKeyCredentialsExpiryOKCount = 0
+ $appKeyCredentialsExpiryOKMoreThan2YearsCount = 0
+ foreach ($appKeyCredential in $getApplication.keyCredentials) {
+ $keyCredentialExpiryTotalDays = (New-TimeSpan -Start $currentDateUTC -End $appKeyCredential.endDateTime).TotalDays
+ if ($keyCredentialExpiryTotalDays -lt 0) {
+ $appKeyCredentialsExpiredCount++
+ }
+ elseif ($keyCredentialExpiryTotalDays -lt $AADServicePrincipalExpiryWarningDays) {
+ $appKeyCredentialsGracePeriodExpiryCount++
}
else {
- $appKeyCredentialsExpiryOKCount++
+ if ($keyCredentialExpiryTotalDays -gt 730) {
+ $appKeyCredentialsExpiryOKMoreThan2YearsCount++
+ }
+ else {
+ $appKeyCredentialsExpiryOKCount++
+ }
}
}
+ $script:htAppDetails.($sp.id).appKeyCredentialsExpiredCount = $appKeyCredentialsExpiredCount
+ $script:htAppDetails.($sp.id).appKeyCredentialsGracePeriodExpiryCount = $appKeyCredentialsGracePeriodExpiryCount
+ $script:htAppDetails.($sp.id).appKeyCredentialsExpiryOKCount = $appKeyCredentialsExpiryOKCount
+ $script:htAppDetails.($sp.id).appKeyCredentialsExpiryOKMoreThan2YearsCount = $appKeyCredentialsExpiryOKMoreThan2YearsCount
}
- $script:htAppDetails.($sp.id).appKeyCredentialsExpiredCount = $appKeyCredentialsExpiredCount
- $script:htAppDetails.($sp.id).appKeyCredentialsGracePeriodExpiryCount = $appKeyCredentialsGracePeriodExpiryCount
- $script:htAppDetails.($sp.id).appKeyCredentialsExpiryOKCount = $appKeyCredentialsExpiryOKCount
- $script:htAppDetails.($sp.id).appKeyCredentialsExpiryOKMoreThan2YearsCount = $appKeyCredentialsExpiryOKMoreThan2YearsCount
}
}
}
-
- } -ThrottleLimit ($ThrottleLimit * 2)
+ } -ThrottleLimit ($ThrottleLimitThis)
$endSPApp = Get-Date
Write-Host "Processing Service Principals - Applications duration: $((New-TimeSpan -Start $startSPApp -End $endSPApp).TotalMinutes) minutes ($((New-TimeSpan -Start $startSPApp -End $endSPApp).TotalSeconds) seconds)"
diff --git a/pwsh/dev/functions/processDataCollection.ps1 b/pwsh/dev/functions/processDataCollection.ps1
index 73888e26..6ac86285 100644
--- a/pwsh/dev/functions/processDataCollection.ps1
+++ b/pwsh/dev/functions/processDataCollection.ps1
@@ -15,13 +15,13 @@ function processDataCollection {
$btchCnt = 0
foreach ($btch in $mgBatch) {
$btchCnt++
- $listOfMGs = @()
+ $listOfMGs = [System.Collections.ArrayList]@()
foreach ($btchMg in $btch.Group | Sort-Object -Property name) {
if ($btchMg.name -eq $btchMg.Properties.displayName) {
- $listOfMGs += $btchMg.name
+ $null = $listOfMGs.Add($btchMg.name)
}
else {
- $listOfMGs += "$($btchMg.name) ($($btchMg.Properties.displayName))"
+ $null = $listOfMGs.Add("$($btchMg.name) ($($btchMg.Properties.displayName))")
}
}
Write-Host " Batch#$($btchCnt) - $($listOfMGs.Count) Management Groups: $($listOfMGs -join ', ')"
@@ -32,8 +32,13 @@ function processDataCollection {
showMemoryUsage
- $batchLevel.Group | ForEach-Object -Parallel {
- $mgdetail = $_
+ $batchSize = [math]::ceiling($batchLevel.Group.Count / $ThrottleLimit)
+ Write-Host "Optimal batch size: $($batchSize)"
+ $counterBatch = [PSCustomObject] @{ Value = 0 }
+ $batchLevelGroupBatch = ($batchLevel.Group) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ Write-Host "Processing data in $($batchLevelGroupBatch.Count) batches"
+
+ $batchLevelGroupBatch | ForEach-Object -Parallel {
#region UsingVARs
#Parameters MG&Sub related
$CsvDelimiter = $using:CsvDelimiter
@@ -108,103 +113,115 @@ function processDataCollection {
#endregion usingVARS
$builtInPolicyDefinitionsCount = $using:builtInPolicyDefinitionsCount
- $addRowToTableDone = $false
+ foreach ($mgdetail in $_.Group) {
- $MgDetailThis = $htManagementGroupsMgPath.($mgdetail.Name)
- $MgParentId = $MgDetailThis.Parent
- $hierarchyLevel = $MgDetailThis.ParentNameChainCount
+ $addRowToTableDone = $false
- if ($MgParentId -eq '__TenantRoot__') {
- $MgParentId = 'TenantRoot'
- $MgParentName = $MgParentId
- }
- else {
- $MgParentName = $htManagementGroupsMgPath.($MgParentId).DisplayName
- }
+ $MgDetailThis = $htManagementGroupsMgPath.($mgdetail.Name)
+ $MgParentId = $MgDetailThis.Parent
+ $hierarchyLevel = $MgDetailThis.ParentNameChainCount
+
+ if ($MgParentId -eq '__TenantRoot__') {
+ $MgParentId = 'TenantRoot'
+ $MgParentName = $MgParentId
+ }
+ else {
+ $MgParentName = $htManagementGroupsMgPath.($MgParentId).DisplayName
+ }
- $rndom = Get-Random -Minimum 10 -Maximum 750
- Start-Sleep -Millisecond $rndom
- $startMgLoopThis = Get-Date
+ $rndom = Get-Random -Minimum 10 -Maximum 750
+ Start-Sleep -Millisecond $rndom
+ $startMgLoopThis = Get-Date
- if ($azAPICallConf['htParameters'].HierarchyMapOnly -eq $false) {
+ if ($azAPICallConf['htParameters'].HierarchyMapOnly -eq $false) {
- #namingValidation
- if (-not [string]::IsNullOrEmpty($mgdetail.properties.displayName)) {
- $namingValidationResult = NamingValidation -toCheck $mgdetail.properties.displayName
- if ($namingValidationResult.Count -gt 0) {
- $script:htNamingValidation.ManagementGroup.($mgdetail.Name) = @{}
- $script:htNamingValidation.ManagementGroup.($mgdetail.Name).nameInvalidChars = ($namingValidationResult -join '')
- $script:htNamingValidation.ManagementGroup.($mgdetail.Name).name = $mgdetail.properties.displayName
+ #namingValidation
+ if (-not [string]::IsNullOrEmpty($mgdetail.properties.displayName)) {
+ $namingValidationResult = NamingValidation -toCheck $mgdetail.properties.displayName
+ if ($namingValidationResult.Count -gt 0) {
+ $script:htNamingValidation.ManagementGroup.($mgdetail.Name) = @{}
+ $script:htNamingValidation.ManagementGroup.($mgdetail.Name).nameInvalidChars = ($namingValidationResult -join '')
+ $script:htNamingValidation.ManagementGroup.($mgdetail.Name).name = $mgdetail.properties.displayName
+ }
}
- }
- $targetMgOrSub = 'MG'
- $baseParameters = @{
- scopeId = $mgdetail.Name
- scopeDisplayName = $mgdetail.properties.displayName
- }
+ $targetMgOrSub = 'MG'
+ $baseParameters = @{
+ scopeId = $mgdetail.Name
+ scopeDisplayName = $mgdetail.properties.displayName
+ }
- #ManagementGroupASCSecureScore
- $mgAscSecureScoreResult = DataCollectionMGSecureScore -Id $mgdetail.Name
+ #ManagementGroupASCSecureScore
+ $mgAscSecureScoreResult = DataCollectionMGSecureScore -Id $mgdetail.Name
- $addRowToTableParameters = @{
- hierarchyLevel = $hierarchyLevel
- mgParentId = $mgParentId
- mgParentName = $mgParentName
- mgAscSecureScoreResult = $mgAscSecureScoreResult
- }
+ $addRowToTableParameters = @{
+ hierarchyLevel = $hierarchyLevel
+ mgParentId = $mgParentId
+ mgParentName = $mgParentName
+ mgAscSecureScoreResult = $mgAscSecureScoreResult
+ }
- #mg diag
- DataCollectionDiagnosticsMG @baseParameters
+ #mg diag
+ DataCollectionDiagnosticsMG @baseParameters
- if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
- #MGPolicyCompliance
- DataCollectionPolicyComplianceStates @baseParameters -TargetMgOrSub $targetMgOrSub
- }
+ if ($azAPICallConf['htParameters'].NoPolicyComplianceStates -eq $false) {
+ #MGPolicyCompliance
+ DataCollectionPolicyComplianceStates @baseParameters -TargetMgOrSub $targetMgOrSub
+ }
- #MGBlueprintDefinitions
- $functionReturn = DataCollectionBluePrintDefinitionsMG @baseParameters @addRowToTableParameters
- if ($functionReturn.'addRowToTableDone') {
- $addRowToTableDone = $true
- }
+ #MGBlueprintDefinitions
+ $functionReturn = DataCollectionBluePrintDefinitionsMG @baseParameters @addRowToTableParameters
+ if ($functionReturn.'addRowToTableDone') {
+ $addRowToTableDone = $true
+ }
- #MGPolicyExemptions
- DataCollectionPolicyExemptions @baseParameters -TargetMgOrSub $targetMgOrSub
+ #MGPolicyExemptions
+ DataCollectionPolicyExemptions @baseParameters -TargetMgOrSub $targetMgOrSub
- #MGPolicyDefinitions
- $functionReturn = DataCollectionPolicyDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
- $policyDefinitionsScopedCount = $functionReturn.'PolicyDefinitionsScopedCount'
+ #MGPolicyDefinitions
+ $functionReturn = DataCollectionPolicyDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
+ $policyDefinitionsScopedCount = $functionReturn.'PolicyDefinitionsScopedCount'
- #MGPolicySetDefinitions
- $functionReturn = DataCollectionPolicySetDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
- $policySetDefinitionsScopedCount = $functionReturn.'PolicySetDefinitionsScopedCount'
+ #MGPolicySetDefinitions
+ $functionReturn = DataCollectionPolicySetDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
+ $policySetDefinitionsScopedCount = $functionReturn.'PolicySetDefinitionsScopedCount'
- if (-not $htMgAtScopePoliciesScoped.($mgdetail.Name)) {
- $script:htMgAtScopePoliciesScoped.($mgdetail.Name) = @{}
- $script:htMgAtScopePoliciesScoped.($mgdetail.Name).ScopedCount = $policyDefinitionsScopedCount + $policySetDefinitionsScopedCount
- }
+ if (-not $htMgAtScopePoliciesScoped.($mgdetail.Name)) {
+ $script:htMgAtScopePoliciesScoped.($mgdetail.Name) = @{}
+ $script:htMgAtScopePoliciesScoped.($mgdetail.Name).ScopedCount = $policyDefinitionsScopedCount + $policySetDefinitionsScopedCount
+ }
- $scopedPolicyCounts = @{
- policyDefinitionsScopedCount = $policyDefinitionsScopedCount
- policySetDefinitionsScopedCount = $policySetDefinitionsScopedCount
- }
+ $scopedPolicyCounts = @{
+ policyDefinitionsScopedCount = $policyDefinitionsScopedCount
+ policySetDefinitionsScopedCount = $policySetDefinitionsScopedCount
+ }
- #MgPolicyAssignments
- $functionReturn = DataCollectionPolicyAssignmentsMG @baseParameters @addRowToTableParameters @scopedPolicyCounts
- if ($functionReturn.'addRowToTableDone') {
- $addRowToTableDone = $true
- }
+ #MgPolicyAssignments
+ $functionReturn = DataCollectionPolicyAssignmentsMG @baseParameters @addRowToTableParameters @scopedPolicyCounts
+ if ($functionReturn.'addRowToTableDone') {
+ $addRowToTableDone = $true
+ }
- #MGRoleDefinitions
- DataCollectionRoleDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
+ #MGRoleDefinitions
+ DataCollectionRoleDefinitions @baseParameters -TargetMgOrSub $targetMgOrSub
- #MGRoleAssignments
- $functionReturn = DataCollectionRoleAssignmentsMG @baseParameters @addRowToTableParameters
- if ($functionReturn.'addRowToTableDone') {
- $addRowToTableDone = $true
- }
+ #MGRoleAssignments
+ $functionReturn = DataCollectionRoleAssignmentsMG @baseParameters @addRowToTableParameters
+ if ($functionReturn.'addRowToTableDone') {
+ $addRowToTableDone = $true
+ }
- if ($addRowToTableDone -ne $true) {
+ if ($addRowToTableDone -ne $true) {
+ addRowToTable `
+ -level $hierarchyLevel `
+ -mgName $mgdetail.properties.displayName `
+ -mgId $mgdetail.Name `
+ -mgParentId $mgParentId `
+ -mgParentName $mgParentName `
+ -mgASCSecureScore $mgAscSecureScoreResult
+ }
+ }
+ else {
addRowToTable `
-level $hierarchyLevel `
-mgName $mgdetail.properties.displayName `
@@ -213,29 +230,19 @@ function processDataCollection {
-mgParentName $mgParentName `
-mgASCSecureScore $mgAscSecureScoreResult
}
- }
- else {
- addRowToTable `
- -level $hierarchyLevel `
- -mgName $mgdetail.properties.displayName `
- -mgId $mgdetail.Name `
- -mgParentId $mgParentId `
- -mgParentName $mgParentName `
- -mgASCSecureScore $mgAscSecureScoreResult
- }
-
- $endMgLoopThis = Get-Date
- $null = $script:customDataCollectionDuration.Add([PSCustomObject]@{
- Type = 'Mg'
- Id = $mgdetail.Name
- DurationSec = (New-TimeSpan -Start $startMgLoopThis -End $endMgLoopThis).TotalSeconds
- })
- $null = $script:arrayDataCollectionProgressMg.Add($mgdetail.Name)
- $progressCount = ($arrayDataCollectionProgressMg).Count
- Write-Host " $($progressCount)/$($allManagementGroupsFromEntitiesChildOfRequestedMgCount) Management Groups processed"
+ $endMgLoopThis = Get-Date
+ $null = $script:customDataCollectionDuration.Add([PSCustomObject]@{
+ Type = 'Mg'
+ Id = $mgdetail.Name
+ DurationSec = (New-TimeSpan -Start $startMgLoopThis -End $endMgLoopThis).TotalSeconds
+ })
+ $null = $script:arrayDataCollectionProgressMg.Add($mgdetail.Name)
+ $progressCount = ($arrayDataCollectionProgressMg).Count
+ Write-Host " $($progressCount)/$($allManagementGroupsFromEntitiesChildOfRequestedMgCount) Management Groups processed"
+ }
} -ThrottleLimit $ThrottleLimit
}
@@ -270,133 +277,128 @@ function processDataCollection {
$startSubLoop = Get-Date
if ($subsToProcessInCustomDataCollectionCount -gt 0) {
+ $batchSize = [math]::ceiling($subsToProcessInCustomDataCollectionCount / $ThrottleLimit)
+ Write-Host "Optimal batch size: $($batchSize)"
$counterBatch = [PSCustomObject] @{ Value = 0 }
- $batchSize = 100
- if ($subsToProcessInCustomDataCollectionCount -gt 500) {
- $batchSize = 200
- }
- Write-Host " Subscriptions Batch size: $batchSize"
-
- $subscriptionsBatch = $subsToProcessInCustomDataCollection | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
- $batchCnt = 0
- foreach ($batch in $subscriptionsBatch) {
- $startBatch = Get-Date
- $batchCnt++
- Write-Host " processing Batch #$batchCnt/$(($subscriptionsBatch | Measure-Object).Count) ($(($batch.Group | Measure-Object).Count) Subscriptions)"
- showMemoryUsage
-
- $batch.Group | ForEach-Object -Parallel {
- $startSubLoopThis = Get-Date
- $childMgSubDetail = $_
- #region UsingVARs
- #Parameters MG&Sub related
- $CsvDelimiter = $using:CsvDelimiter
- $CsvDelimiterOpposite = $using:CsvDelimiterOpposite
- #Parameters Sub related
- #fromOtherFunctions
- $azAPICallConf = $using:azAPICallConf
- $scriptPath = $using:ScriptPath
- #Array&HTs
- $newTable = $using:newTable
- $storageAccounts = $using:storageAccounts
- $resourcesAll = $using:resourcesAll
- $resourcesIdsAll = $using:resourcesIdsAll
- $resourceGroupsAll = $using:resourceGroupsAll
- $customDataCollectionDuration = $using:customDataCollectionDuration
- $htSubscriptionsMgPath = $using:htSubscriptionsMgPath
- $htManagementGroupsMgPath = $using:htManagementGroupsMgPath
- $htResourceProvidersAll = $using:htResourceProvidersAll
- $arrayFeaturesAll = $using:arrayFeaturesAll
- $htSubscriptionTagList = $using:htSubscriptionTagList
- $htResourceTypesUniqueResource = $using:htResourceTypesUniqueResource
- $htAllTagList = $using:htAllTagList
- $htSubscriptionTags = $using:htSubscriptionTags
- $htCacheDefinitionsPolicy = $using:htCacheDefinitionsPolicy
- $htCacheDefinitionsPolicySet = $using:htCacheDefinitionsPolicySet
- $htCacheDefinitionsRole = $using:htCacheDefinitionsRole
- $htCacheDefinitionsBlueprint = $using:htCacheDefinitionsBlueprint
- $htRoleDefinitionIdsUsedInPolicy = $using:htRoleDefinitionIdsUsedInPolicy
- $htCachePolicyComplianceSUB = $using:htCachePolicyComplianceSUB
- $htCachePolicyComplianceResponseTooLargeSUB = $using:htCachePolicyComplianceResponseTooLargeSUB
- $htCacheAssignmentsRole = $using:htCacheAssignmentsRole
- $htCacheAssignmentsRBACOnResourceGroupsAndResources = $using:htCacheAssignmentsRBACOnResourceGroupsAndResources
- $htCacheAssignmentsBlueprint = $using:htCacheAssignmentsBlueprint
- $htCacheAssignmentsPolicyOnResourceGroupsAndResources = $using:htCacheAssignmentsPolicyOnResourceGroupsAndResources
- $htCacheAssignmentsPolicy = $using:htCacheAssignmentsPolicy
- $htPolicyAssignmentExemptions = $using:htPolicyAssignmentExemptions
- $htResourceLocks = $using:htResourceLocks
- $LimitPOLICYPolicyDefinitionsScopedSubscription = $using:LimitPOLICYPolicyDefinitionsScopedSubscription
- $LimitPOLICYPolicySetDefinitionsScopedSubscription = $using:LimitPOLICYPolicySetDefinitionsScopedSubscription
- $LimitPOLICYPolicyAssignmentsSubscription = $using:LimitPOLICYPolicyAssignmentsSubscription
- $LimitPOLICYPolicySetAssignmentsSubscription = $using:LimitPOLICYPolicySetAssignmentsSubscription
- $childrenSubscriptionsCount = $using:childrenSubscriptionsCount
- $subsToProcessInCustomDataCollectionCount = $using:subsToProcessInCustomDataCollectionCount
- $arrayDataCollectionProgressSub = $using:arrayDataCollectionProgressSub
- $arraySubResourcesAddArrayDuration = $using:arraySubResourcesAddArrayDuration
- $htAllSubscriptionsFromAPI = $using:htAllSubscriptionsFromAPI
- $arrayEntitiesFromAPI = $using:arrayEntitiesFromAPI
- $arrayDiagnosticSettingsMgSub = $using:arrayDiagnosticSettingsMgSub
- $htMgASCSecureScore = $using:htMgASCSecureScore
- $htRoleAssignmentsFromAPIInheritancePrevention = $using:htRoleAssignmentsFromAPIInheritancePrevention
- $htNamingValidation = $using:htNamingValidation
- $htPrincipals = $using:htPrincipals
- $htServicePrincipals = $using:htServicePrincipals
- $htUserTypesGuest = $using:htUserTypesGuest
- $arrayDefenderPlans = $using:arrayDefenderPlans
- $arrayDefenderPlansSubscriptionsSkipped = $using:arrayDefenderPlansSubscriptionsSkipped
- $arrayUserAssignedIdentities4Resources = $using:arrayUserAssignedIdentities4Resources
- $htSubscriptionsRoleAssignmentLimit = $using:htSubscriptionsRoleAssignmentLimit
- $arrayPsRule = $using:arrayPsRule
- $arrayPSRuleTracking = $using:arrayPSRuleTracking
- $htClassicAdministrators = $using:htClassicAdministrators
- $htRoleAssignmentsPIM = $using:htRoleAssignmentsPIM
- $alzPolicies = $using:alzPolicies
- $alzPolicySets = $using:alzPolicySets
- $alzPolicyHashes = $using:alzPolicyHashes
- $alzPolicySetHashes = $using:alzPolicySetHashes
- $htDoARMRoleAssignmentScheduleInstances = $using:htDoARMRoleAssignmentScheduleInstances
- $htDefenderEmailContacts = $using:htDefenderEmailContacts
- $arrayVNets = $using:arrayVNets
- $arrayPrivateEndPoints = $using:arrayPrivateEndPoints
- $htResourceProvidersRef = $using:htResourceProvidersRef
- $arrayPrivateEndPointsFromResourceProperties = $using:arrayPrivateEndPointsFromResourceProperties
- $htResourcePropertiesConvertfromJSONFailed = $using:htResourcePropertiesConvertfromJSONFailed
- $htAvailablePrivateEndpointTypes = $using:htAvailablePrivateEndpointTypes
- $arrayAdvisorScores = $using:arrayAdvisorScores
- $ValidPolicyEffects = $using:ValidPolicyEffects
- #$htResourcesWithProperties = $using:htResourcesWithProperties
- #other
- $function:addRowToTable = $using:funcAddRowToTable
- $function:namingValidation = $using:funcNamingValidation
- $function:resolveObjectIds = $using:funcResolveObjectIds
- $function:testGuid = $using:funcTestGuid
- $function:dataCollectionMGSecureScore = $using:funcDataCollectionMGSecureScore
- $function:dataCollectionDefenderPlans = $using:funcDataCollectionDefenderPlans
- $function:dataCollectionDiagnosticsSub = $using:funcDataCollectionDiagnosticsSub
- $function:dataCollectionResources = $using:funcDataCollectionResources
- $function:dataCollectionStorageAccounts = $using:funcDataCollectionStorageAccounts
- $function:dataCollectionResourceGroups = $using:funcDataCollectionResourceGroups
- $function:dataCollectionResourceProviders = $using:funcDataCollectionResourceProviders
- $function:dataCollectionFeatures = $using:funcDataCollectionFeatures
- $function:dataCollectionResourceLocks = $using:funcDataCollectionResourceLocks
- $function:dataCollectionTags = $using:funcDataCollectionTags
- $function:dataCollectionPolicyComplianceStates = $using:funcDataCollectionPolicyComplianceStates
- $function:dataCollectionASCSecureScoreSub = $using:funcDataCollectionASCSecureScoreSub
- $function:dataCollectionBluePrintDefinitionsSub = $using:funcDataCollectionBluePrintDefinitionsSub
- $function:dataCollectionBluePrintAssignmentsSub = $using:funcDataCollectionBluePrintAssignmentsSub
- $function:dataCollectionPolicyExemptions = $using:funcDataCollectionPolicyExemptions
- $function:dataCollectionPolicyDefinitions = $using:funcDataCollectionPolicyDefinitions
- $function:dataCollectionPolicySetDefinitions = $using:funcDataCollectionPolicySetDefinitions
- $function:dataCollectionPolicyAssignmentsSub = $using:funcDataCollectionPolicyAssignmentsSub
- $function:dataCollectionRoleDefinitions = $using:funcDataCollectionRoleDefinitions
- $function:dataCollectionRoleAssignmentsSub = $using:funcDataCollectionRoleAssignmentsSub
- $function:dataCollectionClassicAdministratorsSub = $using:funcDataCollectionClassicAdministratorsSub
- $function:dataCollectionDefenderEmailContacts = $using:funcDataCollectionDefenderEmailContacts
- $function:dataCollectionVNets = $using:funcDataCollectionVNets
- $function:dataCollectionPrivateEndpoints = $using:funcDataCollectionPrivateEndpoints
- $function:dataCollectionAdvisorScores = $using:funcDataCollectionAdvisorScores
- $function:detectPolicyEffect = $using:funcDetectPolicyEffect
- #endregion UsingVARs
+ $subsToProcessInCustomDataCollectionBatch = ($subsToProcessInCustomDataCollection) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ Write-Host "Processing data in $($subsToProcessInCustomDataCollectionBatch.Count) batches"
+
+ $startBatch = Get-Date
+ showMemoryUsage
+
+ $subsToProcessInCustomDataCollectionBatch | ForEach-Object -Parallel {
+ $startSubLoopThis = Get-Date
+ #region UsingVARs
+ #Parameters MG&Sub related
+ $CsvDelimiter = $using:CsvDelimiter
+ $CsvDelimiterOpposite = $using:CsvDelimiterOpposite
+ #Parameters Sub related
+ #fromOtherFunctions
+ $azAPICallConf = $using:azAPICallConf
+ $scriptPath = $using:ScriptPath
+ #Array&HTs
+ $newTable = $using:newTable
+ $storageAccounts = $using:storageAccounts
+ $resourcesAll = $using:resourcesAll
+ $resourcesIdsAll = $using:resourcesIdsAll
+ $resourceGroupsAll = $using:resourceGroupsAll
+ $customDataCollectionDuration = $using:customDataCollectionDuration
+ $htSubscriptionsMgPath = $using:htSubscriptionsMgPath
+ $htManagementGroupsMgPath = $using:htManagementGroupsMgPath
+ $htResourceProvidersAll = $using:htResourceProvidersAll
+ $arrayFeaturesAll = $using:arrayFeaturesAll
+ $htSubscriptionTagList = $using:htSubscriptionTagList
+ $htResourceTypesUniqueResource = $using:htResourceTypesUniqueResource
+ $htAllTagList = $using:htAllTagList
+ $htSubscriptionTags = $using:htSubscriptionTags
+ $htCacheDefinitionsPolicy = $using:htCacheDefinitionsPolicy
+ $htCacheDefinitionsPolicySet = $using:htCacheDefinitionsPolicySet
+ $htCacheDefinitionsRole = $using:htCacheDefinitionsRole
+ $htCacheDefinitionsBlueprint = $using:htCacheDefinitionsBlueprint
+ $htRoleDefinitionIdsUsedInPolicy = $using:htRoleDefinitionIdsUsedInPolicy
+ $htCachePolicyComplianceSUB = $using:htCachePolicyComplianceSUB
+ $htCachePolicyComplianceResponseTooLargeSUB = $using:htCachePolicyComplianceResponseTooLargeSUB
+ $htCacheAssignmentsRole = $using:htCacheAssignmentsRole
+ $htCacheAssignmentsRBACOnResourceGroupsAndResources = $using:htCacheAssignmentsRBACOnResourceGroupsAndResources
+ $htCacheAssignmentsBlueprint = $using:htCacheAssignmentsBlueprint
+ $htCacheAssignmentsPolicyOnResourceGroupsAndResources = $using:htCacheAssignmentsPolicyOnResourceGroupsAndResources
+ $htCacheAssignmentsPolicy = $using:htCacheAssignmentsPolicy
+ $htPolicyAssignmentExemptions = $using:htPolicyAssignmentExemptions
+ $htResourceLocks = $using:htResourceLocks
+ $LimitPOLICYPolicyDefinitionsScopedSubscription = $using:LimitPOLICYPolicyDefinitionsScopedSubscription
+ $LimitPOLICYPolicySetDefinitionsScopedSubscription = $using:LimitPOLICYPolicySetDefinitionsScopedSubscription
+ $LimitPOLICYPolicyAssignmentsSubscription = $using:LimitPOLICYPolicyAssignmentsSubscription
+ $LimitPOLICYPolicySetAssignmentsSubscription = $using:LimitPOLICYPolicySetAssignmentsSubscription
+ $childrenSubscriptionsCount = $using:childrenSubscriptionsCount
+ $subsToProcessInCustomDataCollectionCount = $using:subsToProcessInCustomDataCollectionCount
+ $arrayDataCollectionProgressSub = $using:arrayDataCollectionProgressSub
+ $arraySubResourcesAddArrayDuration = $using:arraySubResourcesAddArrayDuration
+ $htAllSubscriptionsFromAPI = $using:htAllSubscriptionsFromAPI
+ $arrayEntitiesFromAPI = $using:arrayEntitiesFromAPI
+ $arrayDiagnosticSettingsMgSub = $using:arrayDiagnosticSettingsMgSub
+ $htMgASCSecureScore = $using:htMgASCSecureScore
+ $htRoleAssignmentsFromAPIInheritancePrevention = $using:htRoleAssignmentsFromAPIInheritancePrevention
+ $htNamingValidation = $using:htNamingValidation
+ $htPrincipals = $using:htPrincipals
+ $htServicePrincipals = $using:htServicePrincipals
+ $htUserTypesGuest = $using:htUserTypesGuest
+ $arrayDefenderPlans = $using:arrayDefenderPlans
+ $arrayDefenderPlansSubscriptionsSkipped = $using:arrayDefenderPlansSubscriptionsSkipped
+ $arrayUserAssignedIdentities4Resources = $using:arrayUserAssignedIdentities4Resources
+ $htSubscriptionsRoleAssignmentLimit = $using:htSubscriptionsRoleAssignmentLimit
+ $arrayPsRule = $using:arrayPsRule
+ $arrayPSRuleTracking = $using:arrayPSRuleTracking
+ $htClassicAdministrators = $using:htClassicAdministrators
+ $htRoleAssignmentsPIM = $using:htRoleAssignmentsPIM
+ $alzPolicies = $using:alzPolicies
+ $alzPolicySets = $using:alzPolicySets
+ $alzPolicyHashes = $using:alzPolicyHashes
+ $alzPolicySetHashes = $using:alzPolicySetHashes
+ $htDoARMRoleAssignmentScheduleInstances = $using:htDoARMRoleAssignmentScheduleInstances
+ $htDefenderEmailContacts = $using:htDefenderEmailContacts
+ $arrayVNets = $using:arrayVNets
+ $arrayPrivateEndPoints = $using:arrayPrivateEndPoints
+ $htResourceProvidersRef = $using:htResourceProvidersRef
+ $arrayPrivateEndPointsFromResourceProperties = $using:arrayPrivateEndPointsFromResourceProperties
+ $htResourcePropertiesConvertfromJSONFailed = $using:htResourcePropertiesConvertfromJSONFailed
+ $htAvailablePrivateEndpointTypes = $using:htAvailablePrivateEndpointTypes
+ $arrayAdvisorScores = $using:arrayAdvisorScores
+ $ValidPolicyEffects = $using:ValidPolicyEffects
+ #$htResourcesWithProperties = $using:htResourcesWithProperties
+ #other
+ $function:addRowToTable = $using:funcAddRowToTable
+ $function:namingValidation = $using:funcNamingValidation
+ $function:resolveObjectIds = $using:funcResolveObjectIds
+ $function:testGuid = $using:funcTestGuid
+ $function:dataCollectionMGSecureScore = $using:funcDataCollectionMGSecureScore
+ $function:dataCollectionDefenderPlans = $using:funcDataCollectionDefenderPlans
+ $function:dataCollectionDiagnosticsSub = $using:funcDataCollectionDiagnosticsSub
+ $function:dataCollectionResources = $using:funcDataCollectionResources
+ $function:dataCollectionStorageAccounts = $using:funcDataCollectionStorageAccounts
+ $function:dataCollectionResourceGroups = $using:funcDataCollectionResourceGroups
+ $function:dataCollectionResourceProviders = $using:funcDataCollectionResourceProviders
+ $function:dataCollectionFeatures = $using:funcDataCollectionFeatures
+ $function:dataCollectionResourceLocks = $using:funcDataCollectionResourceLocks
+ $function:dataCollectionTags = $using:funcDataCollectionTags
+ $function:dataCollectionPolicyComplianceStates = $using:funcDataCollectionPolicyComplianceStates
+ $function:dataCollectionASCSecureScoreSub = $using:funcDataCollectionASCSecureScoreSub
+ $function:dataCollectionBluePrintDefinitionsSub = $using:funcDataCollectionBluePrintDefinitionsSub
+ $function:dataCollectionBluePrintAssignmentsSub = $using:funcDataCollectionBluePrintAssignmentsSub
+ $function:dataCollectionPolicyExemptions = $using:funcDataCollectionPolicyExemptions
+ $function:dataCollectionPolicyDefinitions = $using:funcDataCollectionPolicyDefinitions
+ $function:dataCollectionPolicySetDefinitions = $using:funcDataCollectionPolicySetDefinitions
+ $function:dataCollectionPolicyAssignmentsSub = $using:funcDataCollectionPolicyAssignmentsSub
+ $function:dataCollectionRoleDefinitions = $using:funcDataCollectionRoleDefinitions
+ $function:dataCollectionRoleAssignmentsSub = $using:funcDataCollectionRoleAssignmentsSub
+ $function:dataCollectionClassicAdministratorsSub = $using:funcDataCollectionClassicAdministratorsSub
+ $function:dataCollectionDefenderEmailContacts = $using:funcDataCollectionDefenderEmailContacts
+ $function:dataCollectionVNets = $using:funcDataCollectionVNets
+ $function:dataCollectionPrivateEndpoints = $using:funcDataCollectionPrivateEndpoints
+ $function:dataCollectionAdvisorScores = $using:funcDataCollectionAdvisorScores
+ $function:detectPolicyEffect = $using:funcDetectPolicyEffect
+ #endregion UsingVARs
+
+ foreach ($childMgSubDetail in $_.Group) {
$addRowToTableDone = $false
@@ -610,12 +612,11 @@ function processDataCollection {
$null = $script:arrayDataCollectionProgressSub.Add($childMgSubId)
$progressCount = ($arrayDataCollectionProgressSub).Count
Write-Host " $($progressCount)/$($subsToProcessInCustomDataCollectionCount) Subscriptions processed"
+ }
+ } -ThrottleLimit $ThrottleLimit
- } -ThrottleLimit $ThrottleLimit
-
- $endBatch = Get-Date
- Write-Host " Batch #$batchCnt processing duration: $((New-TimeSpan -Start $startBatch -End $endBatch).TotalMinutes) minutes ($((New-TimeSpan -Start $startBatch -End $endBatch).TotalSeconds) seconds)"
- }
+ $endBatch = Get-Date
+ Write-Host " Batch #$batchCnt processing duration: $((New-TimeSpan -Start $startBatch -End $endBatch).TotalMinutes) minutes ($((New-TimeSpan -Start $startBatch -End $endBatch).TotalSeconds) seconds)"
$endSubLoop = Get-Date
Write-Host " CustomDataCollection Subscriptions processing duration: $((New-TimeSpan -Start $startSubLoop -End $endSubLoop).TotalMinutes) minutes ($((New-TimeSpan -Start $startSubLoop -End $endSubLoop).TotalSeconds) seconds)"
@@ -625,7 +626,6 @@ function processDataCollection {
Write-Host " CustomDataCollection Subscriptions 'PSRule for Azure' processing duration (in sum): $($durationPSRuleTotalSeconds / 60) minutes ($($durationPSRuleTotalSeconds) seconds)"
}
}
- #test
Write-Host " built-in PolicyDefinitions: $($($htCacheDefinitionsPolicy).Values.where({$_.Type -eq 'BuiltIn'}).Count)"
Write-Host " custom PolicyDefinitions: $($($htCacheDefinitionsPolicy).Values.where({$_.Type -eq 'Custom'}).Count)"
Write-Host " all PolicyDefinitions: $($($htCacheDefinitionsPolicy).Values.Count)"
diff --git a/pwsh/dev/functions/processDefinitionInsights.ps1 b/pwsh/dev/functions/processDefinitionInsights.ps1
index 3361cf33..16f2d925 100644
--- a/pwsh/dev/functions/processDefinitionInsights.ps1
+++ b/pwsh/dev/functions/processDefinitionInsights.ps1
@@ -57,9 +57,8 @@ function processDefinitionInsights() {
($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId).Assignments = [array]($htPoliciesWithAssignmentOnRgRes.($customPolicy.PolicyDefinitionId).Assignments)
}
else {
- $array = @()
- $array += ($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId).Assignments
- $array += $htPoliciesWithAssignmentOnRgRes.($customPolicy.PolicyDefinitionId).Assignments
+ $array = [System.Collections.ArrayList]@()
+ $null = $array.Add($htPoliciesWithAssignmentOnRgRes.($customPolicy.PolicyDefinitionId).Assignments)
($htPolicyWithAssignments).policy.($customPolicy.PolicyDefinitionId).Assignments = $array
}
}
@@ -72,9 +71,8 @@ function processDefinitionInsights() {
($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId).Assignments = [array]($htPoliciesWithAssignmentOnRgRes.($customPolicySet.PolicyDefinitionId).Assignments)
}
else {
- $array = @()
- $array += ($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId).Assignments
- $array += $htPoliciesWithAssignmentOnRgRes.($customPolicySet.PolicyDefinitionId).Assignments
+ $array = [System.Collections.ArrayList]@()
+ $null = $array.Add($htPoliciesWithAssignmentOnRgRes.($customPolicySet.PolicyDefinitionId).Assignments)
($htPolicyWithAssignments).policySet.($customPolicySet.PolicyDefinitionId).Assignments = $array
}
}
@@ -843,7 +841,7 @@ tf.init();}}
$null = $arrayRoleDefinitionsForCSVExport.Add([PSCustomObject]@{
Name = $role.Name
Id = $role.Id
- Description = $role.Json.description
+ Description = $role.Json.properties.description
Type = $roleType
AssignmentsCount = $assignmentsCount
AssignableScopesCount = $AssignableScopesCount
diff --git a/pwsh/dev/functions/processNetwork.ps1 b/pwsh/dev/functions/processNetwork.ps1
index a24ad516..2b2257fc 100644
--- a/pwsh/dev/functions/processNetwork.ps1
+++ b/pwsh/dev/functions/processNetwork.ps1
@@ -76,7 +76,7 @@ function processNetwork {
}
}
else {
- $arrayRemoteMGPath = @()
+ $arrayRemoteMGPath = [System.Collections.ArrayList]@()
foreach ($remoteId in $remoteTenantId) {
if ($remoteId -eq 'SubscriptionNotFound Tenant unknown') {
$remoteMGPath = 'unknown'
@@ -86,10 +86,10 @@ function processNetwork {
$objectGuid = [System.Guid]::empty
if ([System.Guid]::TryParse($remoteId, [System.Management.Automation.PSReference]$ObjectGuid)) {
if ($remoteId -in $MSTenantIds) {
- $arrayRemoteMGPath += "$remoteId (MS)"
+ $null = $arrayRemoteMGPath.Add("$remoteId (MS)")
}
else {
- $arrayRemoteMGPath += $remoteId
+ $null = $arrayRemoteMGPath.Add($remoteId)
}
if ($remoteId -eq $azApiCallConf['checkcontext'].tenant.id) {
$peeringXTenant = 'false'
@@ -378,7 +378,7 @@ function processNetwork {
$Mask = $AddressPrefix.substring($AddressPrefix.Length - 2, 2)
#Amount of available IP Addresses minus the 3 IPs that Azure consumes, minus net and broadcast
- #https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-faq#are-there-any-restrictions-on-using-ip-addresses-within-these-subnets
+ #https://learn.microsoft.com/azure/virtual-network/virtual-networks-faq#are-there-any-restrictions-on-using-ip-addresses-within-these-subnets
switch ($Mask) {
'30' { $AvailableAddresses = [Math]::Pow(2, 2) - 5 }
'29' { $AvailableAddresses = [Math]::Pow(2, 3) - 5 }
diff --git a/pwsh/dev/functions/processPrivateEndpoints.ps1 b/pwsh/dev/functions/processPrivateEndpoints.ps1
index 7344e782..f54eedcb 100644
--- a/pwsh/dev/functions/processPrivateEndpoints.ps1
+++ b/pwsh/dev/functions/processPrivateEndpoints.ps1
@@ -39,15 +39,15 @@ function processPrivateEndpoints {
else {
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($peSubscriptionId)?api-version=2020-01-01"
$remoteTenantId = AzAPICall -AzAPICallConfiguration $azApiCallConf -uri $uri -listenOn 'content' -currentTask "getTenantId for subscriptionId '$($peSubscriptionId)'"
- $arrayRemoteMGPath = @()
+ $arrayRemoteMGPath = [System.Collections.ArrayList]@()
foreach ($remoteId in $remoteTenantId) {
$objectGuid = [System.Guid]::empty
if ([System.Guid]::TryParse($remoteId, [System.Management.Automation.PSReference]$ObjectGuid)) {
if ($remoteId -in $MSTenantIds) {
- $arrayRemoteMGPath += "$remoteId (MS)"
+ $null = $arrayRemoteMGPath.Add("$remoteId (MS)")
}
else {
- $arrayRemoteMGPath += $remoteId
+ $null = $arrayRemoteMGPath.Add($remoteId)
}
if ($remoteId -eq $azApiCallConf['checkcontext'].tenant.id) {
$peXTenant = $false
@@ -198,15 +198,15 @@ function processPrivateEndpoints {
else {
$uri = "$($azAPICallConf['azAPIEndpointUrls'].ARM)/subscriptions/$($resourceSubscriptionId)?api-version=2020-01-01"
$remoteTenantId = AzAPICall -AzAPICallConfiguration $azApiCallConf -uri $uri -listenOn 'content' -currentTask "getTenantId for subscriptionId '$($resourceSubscriptionId)'"
- $arrayRemoteMGPath = @()
+ $arrayRemoteMGPath = [System.Collections.ArrayList]@()
foreach ($remoteId in $remoteTenantId) {
$objectGuid = [System.Guid]::empty
if ([System.Guid]::TryParse($remoteId, [System.Management.Automation.PSReference]$ObjectGuid)) {
if ($remoteId -in $MSTenantIds) {
- $arrayRemoteMGPath += "$remoteId (MS)"
+ $null = $arrayRemoteMGPath.Add("$remoteId (MS)")
}
else {
- $arrayRemoteMGPath += $remoteId
+ $null = $arrayRemoteMGPath.Add($remoteId)
}
if ($remoteId -eq $azApiCallConf['checkcontext'].tenant.id) {
$resourceXTenant = $false
diff --git a/pwsh/dev/functions/processScopeInsightsMgOrSub.ps1 b/pwsh/dev/functions/processScopeInsightsMgOrSub.ps1
index 9ed6cc45..ddadf898 100644
--- a/pwsh/dev/functions/processScopeInsightsMgOrSub.ps1
+++ b/pwsh/dev/functions/processScopeInsightsMgOrSub.ps1
@@ -145,7 +145,7 @@ function processScopeInsightsMgOrSub($mgOrSub, $mgChild, $subscriptionId, $subsc
Subscription Path: $subPath
State: $subscriptionState
QuotaId: $subscriptionQuotaId
- Microsoft Defender for Cloud Secure Score: $subscriptionASCPoints Video , Blog , docs
+ Microsoft Defender for Cloud Secure Score: $subscriptionASCPoints Video , Blog , learn
Microsoft Defender for Cloud 'Email notifications' state: $MDfCEmailNotificationsState
Microsoft Defender for Cloud 'Email notifications' severity: $MDfCEmailNotificationsSeverity
Microsoft Defender for Cloud 'Email notifications' roles: $MDfCEmailNotificationsRoles
@@ -173,18 +173,18 @@ function processScopeInsightsMgOrSub($mgOrSub, $mgChild, $subscriptionId, $subsc
$htmlTableId = "ScopeInsights_DefenderPlans_$($subscriptionId -replace '-','_')"
$randomFunctionName = "func_$htmlTableId"
[void]$htmlScopeInsights.AppendLine(@"
- Microsoft Defender for Cloud plans docs
+ Microsoft Defender for Cloud plans learn
"@)
if ($defenderPlanSubscriptionDeprecatedContainerRegistry) {
[void]$htmlScopeInsights.AppendLine(@'
-
Using deprecated plan 'Container registries'
docs
+
Using deprecated plan 'Container registries'
learn
'@)
}
if ($defenderPlanSubscriptionDeprecatedKubernetesService) {
[void]$htmlScopeInsights.AppendLine(@'
-
Using deprecated plan 'Kubernetes'
docs
+
Using deprecated plan 'Kubernetes'
learn
'@)
}
@@ -286,7 +286,7 @@ tf.init();}}
if ($subscriptionSkippedMDfC.Count -gt 0) {
if ($subscriptionSkippedMDfC.reason -eq 'SubScriptionNotRegistered') {
[void]$htmlScopeInsights.AppendLine(@"
-
Microsoft Defender for Cloud plans - Subscription skipped ($($subscriptionSkippedMDfC.reason)) (ResourceProvider: Microsoft.Security)
docs
+
Microsoft Defender for Cloud plans - Subscription skipped ($($subscriptionSkippedMDfC.reason)) (ResourceProvider: Microsoft.Security)
learn
"@)
}
else {
@@ -298,7 +298,7 @@ tf.init();}}
}
else {
[void]$htmlScopeInsights.AppendLine(@'
-
No Microsoft Defender for Cloud plans
docs
+
No Microsoft Defender for Cloud plans
learn
'@)
}
}
@@ -440,7 +440,7 @@ tf.init();}}
}
else {
[void]$htmlScopeInsights.AppendLine(@'
-
No Subscription Diagnostic settings
docs
+
No Subscription Diagnostic settings
learn
'@)
}
[void]$htmlScopeInsights.AppendLine(@'
@@ -562,7 +562,7 @@ extensions: [{ name: 'sort' }]
Tag Name Usage ($tagNamesUniqueCount unique Tag Names applied at $($tagNamesUsedInScopes)
-
Resource naming and tagging decision guide docs
+
Resource naming and tagging decision guide learn
Download CSV
semicolon |
comma
@@ -636,7 +636,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlScopeInsights.AppendLine(@"
- Tag Name Usage ($tagsUsageCount Tags) docs
+ Tag Name Usage ($tagsUsageCount Tags) learn
"@)
}
[void]$htmlScopeInsights.AppendLine(@'
@@ -920,7 +920,7 @@ extensions: [{ name: 'sort' }]
$tfCount enabled Subscription Features
-
Set up preview features in Azure subscription docs
+
Set up preview features in Azure subscription learn
@@ -986,7 +986,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlScopeInsights.AppendLine(@'
- 0 enabled Subscription Features docs
+ 0 enabled Subscription Features learn
'@)
}
[void]$htmlScopeInsights.AppendLine(@'
@@ -1013,7 +1013,7 @@ extensions: [{ name: 'sort' }]
Resource Locks
-
Considerations before applying locks docs
+
Considerations before applying locks learn
@@ -1081,7 +1081,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlScopeInsights.AppendLine(@'
- 0 Resource Locks docs
+ 0 Resource Locks learn
'@)
}
[void]$htmlScopeInsights.AppendLine(@'
@@ -1099,7 +1099,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlScopeInsights.AppendLine(@"
$(($mgAllChildMgs).count -1) ManagementGroups below this scope
$(($mgAllChildSubscriptions).count) Subscriptions below this scope
- Microsoft Defender for Cloud Secure Score: $managementGroupASCPoints Video , Blog , docs
+ Microsoft Defender for Cloud Secure Score: $managementGroupASCPoints Video , Blog , learn
"@)
@@ -1235,7 +1235,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlScopeInsights.AppendLine(@'
- No Management Group Diagnostic settings docs
+ No Management Group Diagnostic settings learn
'@)
}
#endregion ScopeInsightsDiagnosticsMg
@@ -1610,7 +1610,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlScopeInsights.AppendLine(@"
CAF Naming Recommendation Compliance
-
CAF - Recommended abbreviations for Azure resource types docs
+
CAF - Recommended abbreviations for Azure resource types learn
Resource details can be found in the CSV output *_ResourcesAll.csv
Download CSV
semicolon |
comma
@@ -2161,7 +2161,7 @@ extensions: [{ name: 'sort' }]
UserAssigned Managed Identities assigned to Resources / vice versa
-
Managed identity 'user-assigned' vs 'system-assigned' docs
+
Managed identity 'user-assigned' vs 'system-assigned' learn
Download CSV
semicolon |
comma
diff --git a/pwsh/dev/functions/processStorageAccountAnalysis.ps1 b/pwsh/dev/functions/processStorageAccountAnalysis.ps1
index 3ed16835..25270e26 100644
--- a/pwsh/dev/functions/processStorageAccountAnalysis.ps1
+++ b/pwsh/dev/functions/processStorageAccountAnalysis.ps1
@@ -22,8 +22,13 @@ function processStorageAccountAnalysis {
}
}
- $storageAccounts | ForEach-Object -Parallel {
- $storageAccount = $_
+ $batchSize = [math]::ceiling($storageAccounts.Count / $ThrottleLimit)
+ Write-Host "Optimal batch size: $($batchSize)"
+ $counterBatch = [PSCustomObject] @{ Value = 0 }
+ $storageAccountsBatch = ($storageAccounts) | Group-Object -Property { [math]::Floor($counterBatch.Value++ / $batchSize) }
+ Write-Host "Processing data in $($storageAccountsBatch.Count) batches"
+
+ $storageAccountsBatch | ForEach-Object -Parallel {
$azAPICallConf = $using:azAPICallConf
$arrayStorageAccountAnalysisResults = $using:arrayStorageAccountAnalysisResults
$htAllSubscriptionsFromAPI = $using:htAllSubscriptionsFromAPI
@@ -33,317 +38,320 @@ function processStorageAccountAnalysis {
$htSACost = $using:htSACost
$StorageAccountAccessAnalysisSubscriptionTags = $using:StorageAccountAccessAnalysisSubscriptionTags
$StorageAccountAccessAnalysisStorageAccountTags = $using:StorageAccountAccessAnalysisStorageAccountTags
- $listContainersSuccess = 'n/a'
- $containersCount = 'n/a'
- $arrayContainers = @()
- $arrayContainersAnonymousContainer = @()
- $arrayContainersAnonymousBlob = @()
- $staticWebsitesState = 'n/a'
- $webSiteResponds = 'n/a'
- $subscriptionId = ($storageAccount.SA.id -split '/')[2]
- $resourceGroupName = ($storageAccount.SA.id -split '/')[4]
- $subDetails = $htAllSubscriptionsFromAPI.($subscriptionId).subDetails
- Write-Host "Processing Storage Account '$($storageAccount.SA.name)' - Subscription: '$($subDetails.displayName)' ($subscriptionId) [$($subDetails.subscriptionPolicies.quotaId)]"
+ foreach ($storageAccount in $_.Group) {
+ $listContainersSuccess = 'n/a'
+ $containersCount = 'n/a'
+ $arrayContainers = [System.Collections.ArrayList]@()
+ $arrayContainersAnonymousContainer = [System.Collections.ArrayList]@()
+ $arrayContainersAnonymousBlob = [System.Collections.ArrayList]@()
+ $staticWebsitesState = 'n/a'
+ $webSiteResponds = 'n/a'
- if ($storageAccount.SA.Properties.primaryEndpoints.blob) {
+ $subscriptionId = ($storageAccount.SA.id -split '/')[2]
+ $resourceGroupName = ($storageAccount.SA.id -split '/')[4]
+ $subDetails = $htAllSubscriptionsFromAPI.($subscriptionId).subDetails
- $urlServiceProps = "$($storageAccount.SA.Properties.primaryEndpoints.blob)?restype=service&comp=properties"
- $saProperties = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $urlServiceProps -method 'GET' -listenOn 'Content' -currentTask "$($storageAccount.SA.name) get restype=service&comp=properties" -saResourceGroupName $resourceGroupName -unhandledErrorAction Continue
- if ($saProperties) {
- if ($saProperties -eq 'AuthorizationFailure' -or $saProperties -eq 'AuthorizationPermissionDenied' -or $saProperties -eq 'ResourceUnavailable' -or $saProperties -eq 'AuthorizationPermissionMismatch' ) {
- if ($saProperties -eq 'ResourceUnavailable') {
- $staticWebsitesState = $saProperties
- }
- }
- else {
- try {
- # ? https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/218#issuecomment-1854516882
- if ($saProperties.gettype().Name -eq 'Byte[]') {
- $byteArray = [byte[]]$saProperties
- $saProperties = [System.Text.Encoding]::UTF8.GetString($byteArray)
- }
+ Write-Host "Processing Storage Account '$($storageAccount.SA.name)' - Subscription: '$($subDetails.displayName)' ($subscriptionId) [$($subDetails.subscriptionPolicies.quotaId)]"
- # $xmlSaProperties = [xml]([string]$saProperties -replace $saProperties.Substring(0, 3)) # Leading character:  (PS version <= 7.3.9)
- # $xmlSaProperties = [xml]([string]$saProperties -replace $saProperties.Substring(0, 1)) # Leading character: or U+feff (PS version >= 7.4.0)
- $xmlSaProperties = [xml]($saProperties -replace '^.*?<', '<') # Universal fix for all PS versions
- if ($xmlSaProperties.StorageServiceProperties.StaticWebsite) {
- if ($xmlSaProperties.StorageServiceProperties.StaticWebsite.Enabled -eq $true) {
- $staticWebsitesState = $true
+ if ($storageAccount.SA.Properties.primaryEndpoints.blob) {
+
+ $urlServiceProps = "$($storageAccount.SA.Properties.primaryEndpoints.blob)?restype=service&comp=properties"
+ $saProperties = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $urlServiceProps -method 'GET' -listenOn 'Content' -currentTask "$($storageAccount.SA.name) get restype=service&comp=properties" -saResourceGroupName $resourceGroupName -unhandledErrorAction Continue
+ if ($saProperties) {
+ if ($saProperties -eq 'AuthorizationFailure' -or $saProperties -eq 'AuthorizationPermissionDenied' -or $saProperties -eq 'ResourceUnavailable' -or $saProperties -eq 'AuthorizationPermissionMismatch' ) {
+ if ($saProperties -eq 'ResourceUnavailable') {
+ $staticWebsitesState = $saProperties
+ }
+ }
+ else {
+ try {
+ # ? https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/218#issuecomment-1854516882
+ if ($saProperties.gettype().Name -eq 'Byte[]') {
+ $byteArray = [byte[]]$saProperties
+ $saProperties = [System.Text.Encoding]::UTF8.GetString($byteArray)
}
- else {
- $staticWebsitesState = $false
+
+ # $xmlSaProperties = [xml]([string]$saProperties -replace $saProperties.Substring(0, 3)) # Leading character:  (PS version <= 7.3.9)
+ # $xmlSaProperties = [xml]([string]$saProperties -replace $saProperties.Substring(0, 1)) # Leading character: or U+feff (PS version >= 7.4.0)
+ $xmlSaProperties = [xml]($saProperties -replace '^.*?<', '<') # Universal fix for all PS versions
+ if ($xmlSaProperties.StorageServiceProperties.StaticWebsite) {
+ if ($xmlSaProperties.StorageServiceProperties.StaticWebsite.Enabled -eq $true) {
+ $staticWebsitesState = $true
+ }
+ else {
+ $staticWebsitesState = $false
+ }
}
}
- }
- catch {
- Write-Host "XMLSAPropertiesFailed: Subscription: $($subDetails.displayName) ($subscriptionId) - Storage Account: $($storageAccount.SA.name)"
- Write-Host $($saProperties.ForEach({ [char]$_ }) -join '') -ForegroundColor Cyan
+ catch {
+ Write-Host "XMLSAPropertiesFailed: Subscription: $($subDetails.displayName) ($subscriptionId) - Storage Account: $($storageAccount.SA.name)"
+ Write-Host $($saProperties.ForEach({ [char]$_ }) -join '') -ForegroundColor Cyan
+ }
}
}
- }
- $urlCompList = "$($storageAccount.SA.Properties.primaryEndpoints.blob)?comp=list"
- $listContainers = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $urlCompList -method 'GET' -listenOn 'Content' -currentTask "$($storageAccount.SA.name) get comp=list" -unhandledErrorAction Continue
- if ($listContainers) {
- if ($listContainers -eq 'AuthorizationFailure' -or $listContainers -eq 'AuthorizationPermissionDenied' -or $listContainers -eq 'ResourceUnavailable' -or $listContainers -eq 'AuthorizationPermissionMismatch') {
- if ($listContainers -eq 'ResourceUnavailable') {
- $listContainersSuccess = $listContainers
+ $urlCompList = "$($storageAccount.SA.Properties.primaryEndpoints.blob)?comp=list"
+ $listContainers = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $urlCompList -method 'GET' -listenOn 'Content' -currentTask "$($storageAccount.SA.name) get comp=list" -unhandledErrorAction Continue
+ if ($listContainers) {
+ if ($listContainers -eq 'AuthorizationFailure' -or $listContainers -eq 'AuthorizationPermissionDenied' -or $listContainers -eq 'ResourceUnavailable' -or $listContainers -eq 'AuthorizationPermissionMismatch') {
+ if ($listContainers -eq 'ResourceUnavailable') {
+ $listContainersSuccess = $listContainers
+ }
+ else {
+ $listContainersSuccess = $false
+ }
}
else {
- $listContainersSuccess = $false
+ $listContainersSuccess = $true
}
- }
- else {
- $listContainersSuccess = $true
- }
- if ($listContainersSuccess -eq $true) {
- # ? https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/218#issuecomment-1854516882
- if ($listContainers.gettype().Name -eq 'Byte[]') {
- $byteArray = [byte[]]$listContainers
- $listContainers = [System.Text.Encoding]::UTF8.GetString($byteArray)
- }
+ if ($listContainersSuccess -eq $true) {
+ # ? https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/218#issuecomment-1854516882
+ if ($listContainers.gettype().Name -eq 'Byte[]') {
+ $byteArray = [byte[]]$listContainers
+ $listContainers = [System.Text.Encoding]::UTF8.GetString($byteArray)
+ }
- # $xmlListContainers = [xml]([string]$listContainers -replace $listContainers.Substring(0, 3)) # Leading character:  (PS version <= 7.3.9)
- # $xmlListContainers = [xml]([string]$listContainers -replace $listContainers.Substring(0, 1)) # Leading character: or U+feff (PS version >= 7.4.0)
- $xmlListContainers = [xml]($listContainers -replace '^.*?<', '<') # Universal fix for all PS versions
- $containersCount = $xmlListContainers.EnumerationResults.Containers.Container.Count
+ # $xmlListContainers = [xml]([string]$listContainers -replace $listContainers.Substring(0, 3)) # Leading character:  (PS version <= 7.3.9)
+ # $xmlListContainers = [xml]([string]$listContainers -replace $listContainers.Substring(0, 1)) # Leading character: or U+feff (PS version >= 7.4.0)
+ $xmlListContainers = [xml]($listContainers -replace '^.*?<', '<') # Universal fix for all PS versions
+ $containersCount = $xmlListContainers.EnumerationResults.Containers.Container.Count
- foreach ($container in $xmlListContainers.EnumerationResults.Containers.Container) {
- $arrayContainers += $container.Name
- if ($container.Name -eq '$web' -and $staticWebsitesState) {
- if ($storageAccount.SA.properties.primaryEndpoints.web) {
- try {
- $testStaticWebsiteResponse = Invoke-WebRequest -Uri $storageAccount.SA.properties.primaryEndpoints.web -Method 'HEAD'
- $webSiteResponds = $true
- }
- catch {
- $webSiteResponds = $false
+ foreach ($container in $xmlListContainers.EnumerationResults.Containers.Container) {
+ $null = $arrayContainers.Add($container.Name)
+ if ($container.Name -eq '$web' -and $staticWebsitesState) {
+ if ($storageAccount.SA.properties.primaryEndpoints.web) {
+ try {
+ $testStaticWebsiteResponse = Invoke-WebRequest -Uri $storageAccount.SA.properties.primaryEndpoints.web -Method 'HEAD'
+ $webSiteResponds = $true
+ }
+ catch {
+ $webSiteResponds = $false
+ }
}
}
- }
- if ($container.Properties.PublicAccess) {
- if ($container.Properties.PublicAccess -eq 'blob') {
- $arrayContainersAnonymousBlob += $container.Name
- }
- if ($container.Properties.PublicAccess -eq 'container') {
- $arrayContainersAnonymousContainer += $container.Name
+ if ($container.Properties.PublicAccess) {
+ if ($container.Properties.PublicAccess -eq 'blob') {
+ $null = $arrayContainersAnonymousBlob.Add($container.Name)
+ }
+ if ($container.Properties.PublicAccess -eq 'container') {
+ $null = $arrayContainersAnonymousContainer.Add($container.Name)
+ }
}
}
}
}
}
- }
- $allowSharedKeyAccess = $storageAccount.SA.properties.allowSharedKeyAccess
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowSharedKeyAccess)) {
- $allowSharedKeyAccess = 'likely True'
- }
- $requireInfrastructureEncryption = $storageAccount.SA.properties.encryption.requireInfrastructureEncryption
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.encryption.requireInfrastructureEncryption)) {
- $requireInfrastructureEncryption = 'likely False'
- }
+ $allowSharedKeyAccess = $storageAccount.SA.properties.allowSharedKeyAccess
+ if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowSharedKeyAccess)) {
+ $allowSharedKeyAccess = 'likely True'
+ }
+ $requireInfrastructureEncryption = $storageAccount.SA.properties.encryption.requireInfrastructureEncryption
+ if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.encryption.requireInfrastructureEncryption)) {
+ $requireInfrastructureEncryption = 'likely False'
+ }
- $arrayResourceAccessRules = [System.Collections.ArrayList]@()
- if ($storageAccount.SA.properties.networkAcls.resourceAccessRules) {
- if ($storageAccount.SA.properties.networkAcls.resourceAccessRules.count -gt 0) {
- foreach ($resourceAccessRule in $storageAccount.SA.properties.networkAcls.resourceAccessRules) {
+ $arrayResourceAccessRules = [System.Collections.ArrayList]@()
+ if ($storageAccount.SA.properties.networkAcls.resourceAccessRules) {
+ if ($storageAccount.SA.properties.networkAcls.resourceAccessRules.count -gt 0) {
+ foreach ($resourceAccessRule in $storageAccount.SA.properties.networkAcls.resourceAccessRules) {
- $resourceAccessRuleResourceIdSplitted = $resourceAccessRule.resourceId -split '/'
- $resourceType = "$($resourceAccessRuleResourceIdSplitted[6])/$($resourceAccessRuleResourceIdSplitted[7])"
+ $resourceAccessRuleResourceIdSplitted = $resourceAccessRule.resourceId -split '/'
+ $resourceType = "$($resourceAccessRuleResourceIdSplitted[6])/$($resourceAccessRuleResourceIdSplitted[7])"
- [regex]$regex = '\*+'
- #$resourceAccessRule.resourceId
- switch ($regex.matches($resourceAccessRule.resourceId).count) {
- { $_ -eq 1 } {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'resourceGroup'
- sort = 3
- })
- }
- { $_ -eq 2 } {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'subscription'
- sort = 2
- })
- }
- { $_ -eq 3 } {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'tenant'
- sort = 1
- })
- }
- default {
- $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
- resourcetype = $resourceType
- range = 'resource'
- resource = $resourceAccessRule.resourceId
- sort = 0
- })
+ [regex]$regex = '\*+'
+ #$resourceAccessRule.resourceId
+ switch ($regex.matches($resourceAccessRule.resourceId).count) {
+ { $_ -eq 1 } {
+ $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
+ resourcetype = $resourceType
+ range = 'resourceGroup'
+ sort = 3
+ })
+ }
+ { $_ -eq 2 } {
+ $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
+ resourcetype = $resourceType
+ range = 'subscription'
+ sort = 2
+ })
+ }
+ { $_ -eq 3 } {
+ $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
+ resourcetype = $resourceType
+ range = 'tenant'
+ sort = 1
+ })
+ }
+ default {
+ $null = $arrayResourceAccessRules.Add([PSCustomObject]@{
+ resourcetype = $resourceType
+ range = 'resource'
+ resource = $resourceAccessRule.resourceId
+ sort = 0
+ })
+ }
}
}
}
}
- }
- $resourceAccessRulesCount = $arrayResourceAccessRules.count
- if ($resourceAccessRulesCount -eq 0) {
- $resourceAccessRules = ''
- }
- else {
- $ht = @{}
- foreach ($accessRulePerRange in $arrayResourceAccessRules | Group-Object -Property range | Sort-Object -Property Name -Descending) {
+ $resourceAccessRulesCount = $arrayResourceAccessRules.count
+ if ($resourceAccessRulesCount -eq 0) {
+ $resourceAccessRules = ''
+ }
+ else {
+ $ht = @{}
+ foreach ($accessRulePerRange in $arrayResourceAccessRules | Group-Object -Property range | Sort-Object -Property Name -Descending) {
- if ($accessRulePerRange.Name -eq 'resource') {
- $arrayResources = @()
- foreach ($resource in $accessRulePerRange.Group.resource | Sort-Object) {
- $arrayResources += $resource
+ if ($accessRulePerRange.Name -eq 'resource') {
+ $arrayResources = [System.Collections.ArrayList]@()
+ foreach ($resource in $accessRulePerRange.Group.resource | Sort-Object) {
+ $null = $arrayResources.Add($resource)
+ }
+ $ht.($accessRulePerRange.Name) = ($arrayResources)
}
- $ht.($accessRulePerRange.Name) = [array]($arrayResources)
- }
- else {
- $arrayResourceTypes = @()
- foreach ($resourceType in $accessRulePerRange.Group.resourceType | Sort-Object) {
- $arrayResourceTypes += $resourceType
+ else {
+ $arrayResourceTypes = [System.Collections.ArrayList]@()
+ foreach ($resourceType in $accessRulePerRange.Group.resourceType | Sort-Object) {
+ $null = $arrayResourceTypes.Add($resourceType)
+ }
+ $ht.($accessRulePerRange.Name) = ($arrayResourceTypes)
}
- $ht.($accessRulePerRange.Name) = [array]($arrayResourceTypes)
}
+ $resourceAccessRules = $ht | ConvertTo-Json
}
- $resourceAccessRules = $ht | ConvertTo-Json
- }
-
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.publicNetworkAccess)) {
- $publicNetworkAccess = 'likely Enabled'
- }
- else {
- $publicNetworkAccess = $storageAccount.SA.properties.publicNetworkAccess
- }
-
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowedCopyScope)) {
- $allowedCopyScope = 'From any Storage Account'
- }
- else {
- $allowedCopyScope = $storageAccount.SA.properties.allowedCopyScope
- }
- if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowCrossTenantReplication)) {
- if ($allowedCopyScope -ne 'From any Storage Account') {
- $allowCrossTenantReplication = "likely False (allowedCopyScope=$allowedCopyScope)"
+ if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.publicNetworkAccess)) {
+ $publicNetworkAccess = 'likely Enabled'
}
else {
- $allowCrossTenantReplication = 'likely True'
+ $publicNetworkAccess = $storageAccount.SA.properties.publicNetworkAccess
}
- }
- else {
- $allowCrossTenantReplication = $storageAccount.SA.properties.allowCrossTenantReplication
- }
- if ($storageAccount.SA.properties.dnsEndpointType) {
- $dnsEndpointType = $storageAccount.SA.properties.dnsEndpointType
- }
- else {
- $dnsEndpointType = 'standard'
- }
+ if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowedCopyScope)) {
+ $allowedCopyScope = 'From any Storage Account'
+ }
+ else {
+ $allowedCopyScope = $storageAccount.SA.properties.allowedCopyScope
+ }
- if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
- if ($htSACost.($storageAccount.SA.id)) {
- $hlpCost = $htSACost.($storageAccount.SA.id)
- $saCost = $hlpCost.costAll
- $saCostCurrency = $hlpCost.currencyAll
- $saCostMeterCategories = $hlpCost.meterCategoryAll
+ if ([string]::IsNullOrWhiteSpace($storageAccount.SA.properties.allowCrossTenantReplication)) {
+ if ($allowedCopyScope -ne 'From any Storage Account') {
+ $allowCrossTenantReplication = "likely False (allowedCopyScope=$allowedCopyScope)"
+ }
+ else {
+ $allowCrossTenantReplication = 'likely True'
+ }
}
else {
- $saCost = 'n/a'
- $saCostCurrency = 'n/a'
- $saCostMeterCategories = 'n/a'
+ $allowCrossTenantReplication = $storageAccount.SA.properties.allowCrossTenantReplication
}
- }
- else {
- $saCost = ''
- $saCostCurrency = ''
- $saCostMeterCategories = ''
- }
- $temp = [System.Collections.ArrayList]@()
- $null = $temp.Add([PSCustomObject]@{
- storageAccount = $storageAccount.SA.name
- kind = $storageAccount.SA.kind
- skuName = $storageAccount.SA.sku.name
- skuTier = $storageAccount.SA.sku.tier
- location = $storageAccount.SA.location
- creationTime = $storageAccount.SA.properties.creationTime
- allowBlobPublicAccess = $storageAccount.SA.properties.allowBlobPublicAccess
- publicNetworkAccess = $publicNetworkAccess
- SubscriptionId = $subscriptionId
- SubscriptionName = $subDetails.displayName
- subscriptionQuotaId = $subDetails.subscriptionPolicies.quotaId
- subscriptionMGPath = $htSubscriptionsMgPath.($subscriptionId).path -join '/'
- resourceGroup = $resourceGroupName
- networkAclsdefaultAction = $storageAccount.SA.properties.networkAcls.defaultAction
- staticWebsitesState = $staticWebsitesState
- staticWebsitesResponse = $webSiteResponds
- containersCanBeListed = $listContainersSuccess
- containersCount = $containersCount
- containers = $arrayContainers -join "$CSVDelimiterOpposite "
- containersAnonymousContainerCount = $arrayContainersAnonymousContainer.Count
- containersAnonymousContainer = $arrayContainersAnonymousContainer -join "$CSVDelimiterOpposite "
- containersAnonymousBlobCount = $arrayContainersAnonymousBlob.Count
- containersAnonymousBlob = $arrayContainersAnonymousBlob -join "$CSVDelimiterOpposite "
- ipRulesCount = $storageAccount.SA.properties.networkAcls.ipRules.Count
- ipRulesIPAddressList = ($storageAccount.SA.properties.networkAcls.ipRules.value | Sort-Object) -join "$CSVDelimiterOpposite "
- virtualNetworkRulesCount = $storageAccount.SA.properties.networkAcls.virtualNetworkRules.Count
- virtualNetworkRulesList = ($storageAccount.SA.properties.networkAcls.virtualNetworkRules.Id | Sort-Object) -join "$CSVDelimiterOpposite "
- resourceAccessRulesCount = $resourceAccessRulesCount
- resourceAccessRules = $resourceAccessRules
- bypass = ($storageAccount.SA.properties.networkAcls.bypass | Sort-Object) -join "$CSVDelimiterOpposite "
- supportsHttpsTrafficOnly = $storageAccount.SA.properties.supportsHttpsTrafficOnly
- minimumTlsVersion = $storageAccount.SA.properties.minimumTlsVersion
- allowSharedKeyAccess = $allowSharedKeyAccess
- requireInfrastructureEncryption = $requireInfrastructureEncryption
- allowedCopyScope = $allowedCopyScope
- allowCrossTenantReplication = $allowCrossTenantReplication
- dnsEndpointType = $dnsEndpointType
- usedCapacity = $storageAccount.SAUsedCapacity
- cost = $saCost
- metercategory = $saCostMeterCategories
- curreny = $saCostCurrency
- })
+ if ($storageAccount.SA.properties.dnsEndpointType) {
+ $dnsEndpointType = $storageAccount.SA.properties.dnsEndpointType
+ }
+ else {
+ $dnsEndpointType = 'standard'
+ }
- if ($StorageAccountAccessAnalysisSubscriptionTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisSubscriptionTags.Count -gt 0) {
- foreach ($subTag4StorageAccountAccessAnalysis in $StorageAccountAccessAnalysisSubscriptionTags) {
- if ($htSubscriptionTags.($subscriptionId).$subTag4StorageAccountAccessAnalysis) {
- $temp | Add-Member -NotePropertyName "SubTag_$subTag4StorageAccountAccessAnalysis" -NotePropertyValue $($htSubscriptionTags.($subscriptionId).$subTag4StorageAccountAccessAnalysis)
+ if ($azAPICallConf['htParameters'].DoAzureConsumption -eq $true) {
+ if ($htSACost.($storageAccount.SA.id)) {
+ $hlpCost = $htSACost.($storageAccount.SA.id)
+ $saCost = $hlpCost.costAll
+ $saCostCurrency = $hlpCost.currencyAll
+ $saCostMeterCategories = $hlpCost.meterCategoryAll
}
else {
- $temp | Add-Member -NotePropertyName "SubTag_$subTag4StorageAccountAccessAnalysis" -NotePropertyValue 'n/a'
+ $saCost = 'n/a'
+ $saCostCurrency = 'n/a'
+ $saCostMeterCategories = 'n/a'
}
}
- }
+ else {
+ $saCost = ''
+ $saCostCurrency = ''
+ $saCostMeterCategories = ''
+ }
- if ($StorageAccountAccessAnalysisStorageAccountTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisStorageAccountTags.Count -gt 0) {
- if ($storageAccount.SA.tags) {
- $htAllSATags = @{}
- foreach ($saTagName in ($storageAccount.SA.tags | Get-Member).where({ $_.MemberType -eq 'NoteProperty' }).Name) {
- $htAllSATags.$saTagName = $storageAccount.SA.tags.$saTagName
+ $temp = [System.Collections.ArrayList]@()
+ $null = $temp.Add([PSCustomObject]@{
+ storageAccount = $storageAccount.SA.name
+ kind = $storageAccount.SA.kind
+ skuName = $storageAccount.SA.sku.name
+ skuTier = $storageAccount.SA.sku.tier
+ location = $storageAccount.SA.location
+ creationTime = $storageAccount.SA.properties.creationTime
+ allowBlobPublicAccess = $storageAccount.SA.properties.allowBlobPublicAccess
+ publicNetworkAccess = $publicNetworkAccess
+ SubscriptionId = $subscriptionId
+ SubscriptionName = $subDetails.displayName
+ subscriptionQuotaId = $subDetails.subscriptionPolicies.quotaId
+ subscriptionMGPath = $htSubscriptionsMgPath.($subscriptionId).path -join '/'
+ resourceGroup = $resourceGroupName
+ networkAclsdefaultAction = $storageAccount.SA.properties.networkAcls.defaultAction
+ staticWebsitesState = $staticWebsitesState
+ staticWebsitesResponse = $webSiteResponds
+ containersCanBeListed = $listContainersSuccess
+ containersCount = $containersCount
+ containers = $arrayContainers -join "$CSVDelimiterOpposite "
+ containersAnonymousContainerCount = $arrayContainersAnonymousContainer.Count
+ containersAnonymousContainer = $arrayContainersAnonymousContainer -join "$CSVDelimiterOpposite "
+ containersAnonymousBlobCount = $arrayContainersAnonymousBlob.Count
+ containersAnonymousBlob = $arrayContainersAnonymousBlob -join "$CSVDelimiterOpposite "
+ ipRulesCount = $storageAccount.SA.properties.networkAcls.ipRules.Count
+ ipRulesIPAddressList = ($storageAccount.SA.properties.networkAcls.ipRules.value | Sort-Object) -join "$CSVDelimiterOpposite "
+ virtualNetworkRulesCount = $storageAccount.SA.properties.networkAcls.virtualNetworkRules.Count
+ virtualNetworkRulesList = ($storageAccount.SA.properties.networkAcls.virtualNetworkRules.Id | Sort-Object) -join "$CSVDelimiterOpposite "
+ resourceAccessRulesCount = $resourceAccessRulesCount
+ resourceAccessRules = $resourceAccessRules
+ bypass = ($storageAccount.SA.properties.networkAcls.bypass | Sort-Object) -join "$CSVDelimiterOpposite "
+ supportsHttpsTrafficOnly = $storageAccount.SA.properties.supportsHttpsTrafficOnly
+ minimumTlsVersion = $storageAccount.SA.properties.minimumTlsVersion
+ allowSharedKeyAccess = $allowSharedKeyAccess
+ requireInfrastructureEncryption = $requireInfrastructureEncryption
+ allowedCopyScope = $allowedCopyScope
+ allowCrossTenantReplication = $allowCrossTenantReplication
+ dnsEndpointType = $dnsEndpointType
+ usedCapacity = $storageAccount.SAUsedCapacity
+ cost = $saCost
+ metercategory = $saCostMeterCategories
+ curreny = $saCostCurrency
+ })
+
+ if ($StorageAccountAccessAnalysisSubscriptionTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisSubscriptionTags.Count -gt 0) {
+ foreach ($subTag4StorageAccountAccessAnalysis in $StorageAccountAccessAnalysisSubscriptionTags) {
+ if ($htSubscriptionTags.($subscriptionId).$subTag4StorageAccountAccessAnalysis) {
+ $temp | Add-Member -NotePropertyName "SubTag_$subTag4StorageAccountAccessAnalysis" -NotePropertyValue $($htSubscriptionTags.($subscriptionId).$subTag4StorageAccountAccessAnalysis)
+ }
+ else {
+ $temp | Add-Member -NotePropertyName "SubTag_$subTag4StorageAccountAccessAnalysis" -NotePropertyValue 'n/a'
+ }
}
}
- foreach ($saTag4StorageAccountAccessAnalysis in $StorageAccountAccessAnalysisStorageAccountTags) {
- if ($htAllSATags.$saTag4StorageAccountAccessAnalysis) {
- $temp | Add-Member -NotePropertyName "SATag_$saTag4StorageAccountAccessAnalysis" -NotePropertyValue $($htAllSATags.$saTag4StorageAccountAccessAnalysis)
+
+ if ($StorageAccountAccessAnalysisStorageAccountTags[0] -ne 'undefined' -and $StorageAccountAccessAnalysisStorageAccountTags.Count -gt 0) {
+ if ($storageAccount.SA.tags) {
+ $htAllSATags = @{}
+ foreach ($saTagName in ($storageAccount.SA.tags | Get-Member).where({ $_.MemberType -eq 'NoteProperty' }).Name) {
+ $htAllSATags.$saTagName = $storageAccount.SA.tags.$saTagName
+ }
}
- else {
- $temp | Add-Member -NotePropertyName "SATag_$saTag4StorageAccountAccessAnalysis" -NotePropertyValue 'n/a'
+ foreach ($saTag4StorageAccountAccessAnalysis in $StorageAccountAccessAnalysisStorageAccountTags) {
+ if ($htAllSATags.$saTag4StorageAccountAccessAnalysis) {
+ $temp | Add-Member -NotePropertyName "SATag_$saTag4StorageAccountAccessAnalysis" -NotePropertyValue $($htAllSATags.$saTag4StorageAccountAccessAnalysis)
+ }
+ else {
+ $temp | Add-Member -NotePropertyName "SATag_$saTag4StorageAccountAccessAnalysis" -NotePropertyValue 'n/a'
+ }
}
}
- }
-
- $null = $script:arrayStorageAccountAnalysisResults.AddRange($temp)
+ $null = $script:arrayStorageAccountAnalysisResults.AddRange($temp)
+ }
} -ThrottleLimit $ThrottleLimit
}
else {
diff --git a/pwsh/dev/functions/processTenantSummary.ps1 b/pwsh/dev/functions/processTenantSummary.ps1
index 2f873437..ec4def9a 100644
--- a/pwsh/dev/functions/processTenantSummary.ps1
+++ b/pwsh/dev/functions/processTenantSummary.ps1
@@ -3129,8 +3129,8 @@ extensions: [{ name: 'sort' }]
$policyType = 'unknown'
$policy = 'unknown'
- $arrayExemptedPolicies = @()
- $arrayExemptedPoliciesCSV = @()
+ $arrayExemptedPolicies = [System.Collections.ArrayList]@()
+ $arrayExemptedPoliciesCSV = [System.Collections.ArrayList]@()
$policiesExempted = $null
$policiesExemptedCSV = $null
$policiesExemptedCSVCount = $null
@@ -3182,8 +3182,8 @@ extensions: [{ name: 'sort' }]
}
}
- $arrayExemptedPolicies += $policyExempted
- $arrayExemptedPoliciesCSV += $policyExemptedCSV
+ $null = $arrayExemptedPolicies.Add($policyExempted)
+ $null = $arrayExemptedPoliciesCSV.Add($policyExemptedCSV)
}
$policiesExempted = "$($arrayExemptedPolicies.Count)/$($policiesTotalCount) ( $(($arrayExemptedPolicies | Sort-Object) -join ' '))"
@@ -3441,7 +3441,9 @@ extensions: [{ name: 'sort' }]
#this
if (-not $htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower())) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()) = @{}
+ $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()) = @{
+ roleassignments = [System.Collections.ArrayList]@()
+ }
}
if (($htCacheDefinitionsRole).($roleAssignment.RoleDefinitionId).IsCustom) {
@@ -3460,12 +3462,15 @@ extensions: [{ name: 'sort' }]
})
#this
- if ($htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments += $array
- }
- else {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = $array
- }
+ # if ($htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments) {
+ # $null = $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
+ # }
+ # else {
+ # #$script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = $array
+ # $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = [System.Collections.ArrayList]@()
+ # $null = $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
+ # }
+ $null = $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
}
}
@@ -3478,7 +3483,9 @@ extensions: [{ name: 'sort' }]
#this
if (-not $htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower())) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()) = @{}
+ $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()) = @{
+ roleassignments = [System.Collections.ArrayList]@()
+ }
}
if (($htCacheDefinitionsRole).($roleAssignment.RoleDefinitionId).IsCustom) {
@@ -3497,12 +3504,15 @@ extensions: [{ name: 'sort' }]
})
#this
- if ($htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments) {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments += $array
- }
- else {
- $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = $array
- }
+ # if ($htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments) {
+ # $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
+ # }
+ # else {
+ # #$script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = $array
+ # $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments = [System.Collections.ArrayList]@()
+ # $null = $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
+ # }
+ $null = $script:htPolicyAssignmentRoleAssignmentMapping.(($mi.policyAssignmentId).ToLower()).roleassignments.Add($array)
}
}
}
@@ -3512,61 +3522,81 @@ extensions: [{ name: 'sort' }]
#endregion PolicyAssignmentsRoleAssignmentMapping
#region PolicyAssignmentsUniqueRelations
- $startPolicyAssignmnetsUniqueRelations = Get-Date
- Write-Host ' processing PolicyAssignmnetsUniqueRelations'
+ $startPolicyAssignmentsUniqueRelations = Get-Date
+ Write-Host ' processing PolicyAssignmentsUniqueRelations'
$htPolicyAssignmentRelatedRoleAssignments = @{}
$htPolicyAssignmentRelatedExemptions = @{}
foreach ($policyAssignmentIdUnique in $policyBaseQueryUniqueAssignments) {
#region relatedRoleAssignments
- $relatedRoleAssignmentsArray = @()
- $relatedRoleAssignmentsArrayClear = @()
+ $relatedRoleAssignmentsArray = [System.Collections.ArrayList]@()
+ $relatedRoleAssignmentsArrayClear = [System.Collections.ArrayList]@()
if ($htPolicyAssignmentRoleAssignmentMappingCount -gt 0) {
- if ($htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId)) {
- foreach ($entry in $htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId).roleassignments) {
+ $policyAssignmentMapping = $htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId)
+ if ($null -ne $policyAssignmentMapping) {
+ foreach ($entry in $policyAssignmentMapping.roleassignments) {
if ($entry.roleDefinitionType -eq 'builtin') {
- $relatedRoleAssignmentsArray += "$($entry.roleDefinitionName) ($($entry.roleAssignmentId))"
+ $null = $relatedRoleAssignmentsArray.Add("$($entry.roleDefinitionName) ($($entry.roleAssignmentId))")
}
else {
- $relatedRoleAssignmentsArray += "$($entry.roleDefinitionName -replace '<', '<' -replace '>', '>') ($($entry.roleAssignmentId))"
+ $null = $relatedRoleAssignmentsArray.Add("$($entry.roleDefinitionName -replace '<', '<' -replace '>', '>') ($($entry.roleAssignmentId))")
}
- $relatedRoleAssignmentsArrayClear += "$($entry.roleDefinitionName) ($($entry.roleAssignmentId))"
+ $null = $relatedRoleAssignmentsArrayClear.Add("$($entry.roleDefinitionName) ($($entry.roleAssignmentId))")
}
}
}
+ # if ($htPolicyAssignmentRoleAssignmentMappingCount -gt 0) {
+ # if ($htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId)) {
+ # foreach ($entry in $htPolicyAssignmentRoleAssignmentMapping.($policyAssignmentIdUnique.PolicyAssignmentId).roleassignments) {
+ # if ($entry.roleDefinitionType -eq 'builtin') {
+ # $null = $relatedRoleAssignmentsArray.Add("$($entry.roleDefinitionName) ($($entry.roleAssignmentId))")
+ # }
+ # else {
+ # $null = $relatedRoleAssignmentsArray.Add("$($entry.roleDefinitionName -replace '<', '<' -replace '>', '>') ($($entry.roleAssignmentId))")
+ # }
+ # $null = $relatedRoleAssignmentsArrayClear.Add("$($entry.roleDefinitionName) ($($entry.roleAssignmentId))")
+ # }
+ # }
+ # }
+
$htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId) = @{}
if (($relatedRoleAssignmentsArray).count -gt 0) {
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignments = ($relatedRoleAssignmentsArray | Sort-Object) -join "$CsvDelimiterOpposite "
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignmentsClear = ($relatedRoleAssignmentsArrayClear | Sort-Object) -join "$CsvDelimiterOpposite "
+ # $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignments = ($relatedRoleAssignmentsArray | Sort-Object) -join "$CsvDelimiterOpposite "
+ # $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignmentsClear = ($relatedRoleAssignmentsArrayClear | Sort-Object) -join "$CsvDelimiterOpposite "
+ $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId) = @{
+ relatedRoleAssignments = ($relatedRoleAssignmentsArray | Sort-Object) -join "$CsvDelimiterOpposite "
+ relatedRoleAssignmentsClear = ($relatedRoleAssignmentsArrayClear | Sort-Object) -join "$CsvDelimiterOpposite "
+ }
}
else {
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignments = 'none'
- $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignmentsClear = 'none'
+ # $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignments = 'none'
+ # $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId).relatedRoleAssignmentsClear = 'none'
+ $htPolicyAssignmentRelatedRoleAssignments.($policyAssignmentIdUnique.PolicyAssignmentId) = @{
+ relatedRoleAssignments = 'none'
+ relatedRoleAssignmentsClear = 'none'
+ }
}
#endregion relatedRoleAssignments
#region exemptions
- $arrayExemptions = @()
+ $arrayExemptions = [System.Collections.ArrayList]@()
foreach ($exemptionId in $htPolicyAssignmentExemptions.keys) {
if ($htPolicyAssignmentExemptions.($exemptionId).exemption.properties.policyAssignmentId -eq $policyAssignmentIdUnique.PolicyAssignmentId) {
- $arrayExemptions += $htPolicyAssignmentExemptions.($exemptionId).exemption
- if (-not $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId)) {
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId) = @{}
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptionsCount = 1
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptions = $arrayExemptions
- }
- else {
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptionsCount += 1
- $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId).exemptions = $arrayExemptions
- }
+ $null = $arrayExemptions.Add($htPolicyAssignmentExemptions.($exemptionId).exemption)
+ }
+ }
+ if ($arrayExemptions.Count -gt 0) {
+ $htPolicyAssignmentRelatedExemptions.($policyAssignmentIdUnique.PolicyAssignmentId) = @{
+ exemptionsCount = $arrayExemptions.Count
+ exemptions = $arrayExemptions
}
}
#endregion exemptions
}
- $endPolicyAssignmnetsUniqueRelations = Get-Date
- Write-Host " PolicyAssignmnetsUniqueRelations processing duration: $((New-TimeSpan -Start $startPolicyAssignmnetsUniqueRelations -End $endPolicyAssignmnetsUniqueRelations).TotalMinutes) minutes ($((New-TimeSpan -Start $startPolicyAssignmnetsUniqueRelations -End $endPolicyAssignmnetsUniqueRelations).TotalSeconds) seconds)"
+ $endPolicyAssignmentsUniqueRelations = Get-Date
+ Write-Host " PolicyAssignmentsUniqueRelations processing duration: $((New-TimeSpan -Start $startPolicyAssignmentsUniqueRelations -End $endPolicyAssignmentsUniqueRelations).TotalMinutes) minutes ($((New-TimeSpan -Start $startPolicyAssignmentsUniqueRelations -End $endPolicyAssignmentsUniqueRelations).TotalSeconds) seconds)"
#endregion PolicyAssignmentsUniqueRelations
#region PolicyAssignmentsAllCreateEnriched
@@ -6233,7 +6263,7 @@ extensions: [{ name: 'sort' }]
$htmlSUMMARYSecurityGuestUserHighPriviledgesAssignments = $null
$htmlSUMMARYSecurityGuestUserHighPriviledgesAssignments = foreach ($highPrivilegedGuestUserRoleAssignment in ($highPrivilegedGuestUserRoleAssignments)) {
if ($highPrivilededGuestUserRoleAssignment.AssignmentType -eq 'indirect') {
- $assignmentInfo = "indirect / AAD Group Membership '$($highPrivilededGuestUserRoleAssignment.AssignmentInheritFrom)'"
+ $assignmentInfo = "indirect / Microsoft Entra group membership '$($highPrivilededGuestUserRoleAssignment.AssignmentInheritFrom)'"
}
else {
$assignmentInfo = 'direct'
@@ -6860,14 +6890,14 @@ extensions: [{ name: 'sort' }]
#region SUMMARYMGdefault
Write-Host ' processing TenantSummary ManagementGroups - default Management Group'
[void]$htmlTenantSummary.AppendLine(@"
- Hierarchy Settings | Default Management Group Id: '$($defaultManagementGroupId) ' docs
+ Hierarchy Settings | Default Management Group Id: '$($defaultManagementGroupId) ' learn
"@)
#endregion SUMMARYMGdefault
#region SUMMARYMGRequireAuthorizationForGroupCreation
Write-Host ' processing TenantSummary ManagementGroups - requireAuthorizationForGroupCreation Management Group'
[void]$htmlTenantSummary.AppendLine(@"
- Hierarchy Settings | Require authorization for Management Group creation: '$($requireAuthorizationForGroupCreation) ' docs
+ Hierarchy Settings | Require authorization for Management Group creation: '$($requireAuthorizationForGroupCreation) ' learn
"@)
#endregion SUMMARYMGRequireAuthorizationForGroupCreation
@@ -6910,12 +6940,12 @@ extensions: [{ name: 'sort' }]
$tfCount = $summarySubscriptionsCount
$htmlTableId = 'TenantSummary_subs'
- $abbr = " "
+ $abbr = " "
[void]$htmlTenantSummary.AppendLine(@"
$($summarySubscriptionsCount) Subscriptions (state: enabled)
-
Supported Microsoft Azure offers docs
-
Understand Microsoft Defender for Cloud Secure Score Video ,
Blog ,
docs
+
Supported Microsoft Azure offers learn
+
Understand Microsoft Defender for Cloud Secure Score Video ,
Blog ,
learn
Download CSV
semicolon |
comma
@@ -7317,11 +7347,11 @@ extensions: [{ name: 'sort' }]
$tfCount = $tagsUsageCount
$htmlTableId = 'TenantSummary_tagsUsage'
[void]$htmlTenantSummary.AppendLine(@"
- Tag Name Usage ($tagNamesUniqueCount unique Tag Names applied at $($tagNamesUsedInScopes))
-
-
Resource naming and tagging decision guide docs
-
Download CSV
semicolon |
comma
-
+ Tag Name Usage ($tagNamesUniqueCount unique Tag Names applied at $($tagNamesUsedInScopes))
+
+
Resource naming and tagging decision guide learn
+
Download CSV
semicolon |
comma
+
Scope
@@ -7394,7 +7424,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- Tag Name Usage ($tagsUsageCount Tags) docs
+ Tag Name Usage ($tagsUsageCount Tags) learn
"@)
}
#endregion SUMMARYTagNameUsage
@@ -7720,7 +7750,7 @@ extensions: [{ name: 'sort' }]
CAF Naming Recommendation Compliance
-
CAF - Recommended abbreviations for Azure resource types docs
+
CAF - Recommended abbreviations for Azure resource types learn
Resource details can be found in the CSV output *_ResourcesAll.csv
Download CSV
semicolon |
comma
@@ -8329,7 +8359,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$tfCount enabled Subscriptions Features
-
Set up preview features in Azure subscription docs
+
Set up preview features in Azure subscription learn
Download CSV
semicolon |
comma
@@ -8406,7 +8436,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@'
- No enabled Subscriptions Features docs
+ No enabled Subscriptions Features learn
'@)
}
$endSubFeatures = Get-Date
@@ -8433,7 +8463,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
Resource Locks
-
Considerations before applying locks docs
+
Considerations before applying locks learn
Note: Detailed information on Resource Locks is provided in the *_ResourceLocks.csv
@@ -8502,7 +8532,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@'
- No Resource Locks at all docs
+ No Resource Locks at all learn
'@)
}
$endResourceLocks = Get-Date
@@ -8522,8 +8552,8 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
Microsoft Defender for Cloud plans - Subscriptions skipped
-
Register Resource Provider 'Microsoft.Security' docs
-
Microsoft Defender for Cloud's enhanced security features docs
+
Register Resource Provider 'Microsoft.Security' learn
+
Microsoft Defender for Cloud's enhanced security features learn
Download CSV
semicolon |
comma
@@ -8621,17 +8651,17 @@ paging: {results_per_page: ['Records: ', [$spectrum]]},/*state: {types: ['local_
if ($defenderPlanDeprecatedContainerRegistry) {
[void]$htmlTenantSummary.AppendLine(@'
- Using deprecated plan 'Container registries' docs
+ Using deprecated plan 'Container registries' learn
'@)
}
if ($defenderPlanDeprecatedKubernetesService) {
[void]$htmlTenantSummary.AppendLine(@'
- Using deprecated plan 'Kubernetes' docs
+ Using deprecated plan 'Kubernetes' learn
'@)
}
[void]$htmlTenantSummary.AppendLine(@"
- Microsoft Defender for Cloud's enhanced security features docs
+ Microsoft Defender for Cloud's enhanced security features learn
Download CSV semicolon | comma
@@ -8703,17 +8733,17 @@ paging: {results_per_page: ['Records: ', [$spectrum]]},/*state: {types: ['local_
if ($defenderPlanDeprecatedContainerRegistry) {
[void]$htmlTenantSummary.AppendLine(@'
- Using deprecated plan 'Container registries' docs
+ Using deprecated plan 'Container registries' learn
'@)
}
if ($defenderPlanDeprecatedKubernetesService) {
[void]$htmlTenantSummary.AppendLine(@'
- Using deprecated plan 'Kubernetes' docs
+ Using deprecated plan 'Kubernetes' learn
'@)
}
[void]$htmlTenantSummary.AppendLine(@"
- Microsoft Defender for Cloud's enhanced security features docs
+ Microsoft Defender for Cloud's enhanced security features learn
Download CSV semicolon | comma
@@ -8880,7 +8910,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
UserAssigned Managed Identities assigned to Resources / vice versa
-
Managed identity 'user-assigned' vs 'system-assigned' docs
+
Managed identity 'user-assigned' vs 'system-assigned' learn
Download CSV
semicolon |
comma
@@ -10345,7 +10375,7 @@ btn_reset: true, highlight_keywords: true, alternate_rows: true, auto_filter: {
[void]$htmlTenantSummary.AppendLine(@"
$diagnosticSettingsMgManagementGroupsCount ($mgsDiagnosticsApplicableCount) Management Groups configured for Diagnostic settings ($diagnosticSettingsMgCount settings)
-
Management Group Diagnostic Settings - Create Or Update - REST API docs
+
Management Group Diagnostic Settings - Create Or Update - REST API learn
Download CSV
semicolon |
comma
@@ -10483,7 +10513,7 @@ extensions: [{ name: 'sort' }]
else {
[void]$htmlTenantSummary.AppendLine(@'
- No Management Groups configured for Diagnostic settings docs
+ No Management Groups configured for Diagnostic settings learn
'@)
}
@@ -10492,9 +10522,9 @@ extensions: [{ name: 'sort' }]
$tfCount = $arrayMgsWithoutDiagnosticsCount
$htmlTableId = 'TenantSummary_NoDiagnosticsManagementGroups'
[void]$htmlTenantSummary.AppendLine(@"
- $arrayMgsWithoutDiagnosticsCount Management Groups NOT configured for Diagnostic settings docs
+ $arrayMgsWithoutDiagnosticsCount Management Groups NOT configured for Diagnostic settings learn
-
Management Group Diagnostic Settings - Create Or Update - REST API docs
+
Management Group Diagnostic Settings - Create Or Update - REST API learn
Download CSV
semicolon |
comma
@@ -10569,7 +10599,7 @@ extensions: [{ name: 'sort' }]
else {
[void]$htmlTenantSummary.AppendLine(@'
- All Management Groups are configured for Diagnostic settings docs
+ All Management Groups are configured for Diagnostic settings learn
'@)
}
#endregion SUMMARYDiagnosticsManagementGroups
@@ -10589,7 +10619,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$diagnosticSettingsSubSubscriptionsCount Subscriptions configured for Diagnostic settings ($diagnosticSettingsSubCount settings)
-
Create diagnostic setting docs
+
Create diagnostic setting learn
Download CSV
semicolon |
comma
@@ -10723,7 +10753,7 @@ extensions: [{ name: 'sort' }]
else {
[void]$htmlTenantSummary.AppendLine(@'
- No Subscriptions configured for Diagnostic settings docs
+ No Subscriptions configured for Diagnostic settings learn
'@)
}
@@ -10734,7 +10764,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$diagnosticSettingsSubNoDiagCount Subscriptions NOT configured for Diagnostic settings
-
Create diagnostic setting docs
+
Create diagnostic setting learn
Download CSV
semicolon |
comma
@@ -10809,7 +10839,7 @@ extensions: [{ name: 'sort' }]
else {
[void]$htmlTenantSummary.AppendLine(@'
- All Subscriptions are configured for Diagnostic settings docs
+ All Subscriptions are configured for Diagnostic settings learn
'@)
}
#endregion SUMMARYDiagnosticsSubscriptions
@@ -10836,7 +10866,7 @@ extensions: [{ name: 'sort' }]
Resources (1st party) Diagnostics capable $resourceTypesDiagnosticsMetricsLogsTrueCount/$resourceTypesDiagnosticsArraySortedCount ResourceTypes ($resourceTypesDiagnosticsMetricsTrueCount Metrics, $resourceTypesDiagnosticsLogsTrueCount Logs)
Create Custom Policies for Azure ResourceTypes that support Diagnostics Logs and Metrics Create-AzDiagPolicy
-
Supported categories for Azure Resource Logs docs
+
Supported categories for Azure Resource Logs learn
Download CSV
semicolon |
comma
@@ -11160,7 +11190,7 @@ extensions: [{ name: 'sort' }]
else {
$resourceCount = '0'
}
- $recommendation = "Create diagnostics policy for this ResourceType. To verify GA check docs "
+ $recommendation = "Create diagnostics policy for this ResourceType. To verify GA check learn "
$null = $diagnosticsPolicyAnalysis.Add([PSCustomObject]@{
Priority = '2-Medium'
PolicyId = 'n/a'
@@ -11195,7 +11225,7 @@ extensions: [{ name: 'sort' }]
ResourceDiagnostics for Logs - Policy Lifecycle recommendations
Create Custom Policies for Azure ResourceTypes that support Diagnostics Logs and Metrics Create-AzDiagPolicy
-
Supported categories for Azure Resource Logs docs
+
Supported categories for Azure Resource Logs learn
@@ -11227,7 +11257,7 @@ extensions: [{ name: 'sort' }]
$($diagnosticsFinding.Recommendation)
- $($diagnosticsFinding.ResourceType)
+ $($diagnosticsFinding.ResourceType)
$($diagnosticsFinding.ResourceTypeCount)
@@ -11372,24 +11402,24 @@ extensions: [{ name: 'sort' }]
#policySets
if ($tenantCustompolicySetsCount -gt (($LimitPOLICYPolicySetDefinitionsScopedTenant * $LimitCriticalPercentage) / 100)) {
[void]$htmlTenantSummary.AppendLine(@"
- PolicySet definitions: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant docs
+ PolicySet definitions: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant learn
"@)
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- PolicySet definitions: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant docs
+ PolicySet definitions: $tenantCustompolicySetsCount/$LimitPOLICYPolicySetDefinitionsScopedTenant learn
"@)
}
#CustomRoleDefinitions
if ($tenantCustomRolesCount -gt (($LimitRBACCustomRoleDefinitionsTenant * $LimitCriticalPercentage) / 100)) {
[void]$htmlTenantSummary.AppendLine(@"
- Custom Role definitions: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant docs
+ Custom Role definitions: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant learn
"@)
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- Custom Role definitions: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant docs
+ Custom Role definitions: $tenantCustomRolesCount/$LimitRBACCustomRoleDefinitionsTenant learn
"@)
}
@@ -11409,7 +11439,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($mgsApproachingLimitPolicyAssignments | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyAssignmentsManagementGroup) for PolicyAssignment
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -11482,7 +11512,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($mgsApproachingLimitPolicyAssignments | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyAssignmentsManagementGroup) for PolicyAssignment docs
+ $(($mgsApproachingLimitPolicyAssignments | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyAssignmentsManagementGroup) for PolicyAssignment learn
"@)
}
#endregion SUMMARYMgsapproachingLimitsPolicyAssignments
@@ -11496,7 +11526,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($mgsApproachingLimitPolicyScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicyDefinitionsScopedManagementGroup) for Policy Scope
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -11569,7 +11599,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $($mgsApproachingLimitPolicyScope.count) Management Groups approaching Limit ($LimitPOLICYPolicyDefinitionsScopedManagementGroup) for Policy Scope docs
+ $($mgsApproachingLimitPolicyScope.count) Management Groups approaching Limit ($LimitPOLICYPolicyDefinitionsScopedManagementGroup) for Policy Scope learn
"@)
}
#endregion SUMMARYMgsapproachingLimitsPolicyScope
@@ -11583,7 +11613,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($mgsApproachingLimitPolicySetScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedManagementGroup) for PolicySet Scope
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -11656,7 +11686,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($mgsApproachingLimitPolicySetScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedManagementGroup) for PolicySet Scope docs
+ $(($mgsApproachingLimitPolicySetScope | Measure-Object).count) Management Groups approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedManagementGroup) for PolicySet Scope learn
"@)
}
#endregion SUMMARYMgsapproachingLimitsPolicySetScope
@@ -11671,7 +11701,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($mgsApproachingRoleAssignmentLimit | Measure-Object).count) Management Groups approaching Limit ($LimitRBACRoleAssignmentsManagementGroup) for RoleAssignment
-
Azure RBAC Limits docs
+
Azure RBAC Limits learn
Download CSV
semicolon |
comma
@@ -11744,7 +11774,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($mgApproachingRoleAssignmentLimit | Measure-Object).count) Management Groups approaching Limit ($LimitRBACRoleAssignmentsManagementGroup) for RoleAssignment docs
+ $(($mgApproachingRoleAssignmentLimit | Measure-Object).count) Management Groups approaching Limit ($LimitRBACRoleAssignmentsManagementGroup) for RoleAssignment learn
"@)
}
#endregion SUMMARYMgsapproachingLimitsRoleAssignment
@@ -11765,7 +11795,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingLimitFromResourceGroupsAll | Measure-Object).count) Subscriptions approaching Limit ($LimitResourceGroups) for ResourceGroups
-
Azure Subscription Resource Group Limit docs
+
Azure Subscription Resource Group Limit learn
Download CSV
semicolon |
comma
@@ -11839,7 +11869,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($subscriptionsApproachingLimitFromResourceGroupsAll | Measure-Object).count) Subscriptions approaching Limit ($LimitResourceGroups) for ResourceGroups docs
+ $(($subscriptionsApproachingLimitFromResourceGroupsAll | Measure-Object).count) Subscriptions approaching Limit ($LimitResourceGroups) for ResourceGroups learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsResourceGroups
@@ -11853,7 +11883,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingLimitTags | Measure-Object).count) Subscriptions approaching Limit ($LimitTagsSubscription) for Tags
-
Azure Subscription Tag Limit docs
+
Azure Subscription Tag Limit learn
Download CSV
semicolon |
comma
@@ -11926,7 +11956,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $($subscriptionsApproachingLimitTags.count) Subscriptions approaching Limit ($LimitTagsSubscription) for Tags docs
+ $($subscriptionsApproachingLimitTags.count) Subscriptions approaching Limit ($LimitTagsSubscription) for Tags learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsSubscriptionTags
@@ -11940,7 +11970,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingLimitPolicyAssignments | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyAssignmentsSubscription) for PolicyAssignment
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -12013,7 +12043,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($subscriptionsApproachingLimitPolicyAssignments | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyAssignmentsSubscription) for PolicyAssignment docs
+ $(($subscriptionsApproachingLimitPolicyAssignments | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyAssignmentsSubscription) for PolicyAssignment learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsPolicyAssignments
@@ -12027,7 +12057,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicyDefinitionsScopedSubscription) for Policy Scope
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -12100,7 +12130,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $($subscriptionsApproachingLimitPolicyScope.count) Subscriptions approaching Limit ($LimitPOLICYPolicyDefinitionsScopedSubscription) for Policy Scope docs
+ $($subscriptionsApproachingLimitPolicyScope.count) Subscriptions approaching Limit ($LimitPOLICYPolicyDefinitionsScopedSubscription) for Policy Scope learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsPolicyScope
@@ -12114,7 +12144,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedSubscription) for PolicySet Scope
-
Azure Policy Limits docs
+
Azure Policy Limits learn
Download CSV
semicolon |
comma
@@ -12187,7 +12217,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedSubscription) for PolicySet Scope docs
+ $(($subscriptionsApproachingLimitPolicyScope | Measure-Object).count) Subscriptions approaching Limit ($LimitPOLICYPolicySetDefinitionsScopedSubscription) for PolicySet Scope learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsPolicySetScope
@@ -12204,7 +12234,7 @@ extensions: [{ name: 'sort' }]
[void]$htmlTenantSummary.AppendLine(@"
$(($subscriptionsApproachingRoleAssignmentLimit | Measure-Object).count) Subscriptions approaching Limit ($($availableSubscriptionsRoleAssignmentLimits)) for RoleAssignment
-
Azure RBAC Limits docs
+
Azure RBAC Limits learn
Download CSV
semicolon |
comma
@@ -12277,7 +12307,7 @@ extensions: [{ name: 'sort' }]
}
else {
[void]$htmlTenantSummary.AppendLine(@"
- $(($subscriptionsApproachingRoleAssignmentLimit | Measure-Object).count) Subscriptions approaching Limit ($availableSubscriptionsRoleAssignmentLimits) for RoleAssignment docs
+ $(($subscriptionsApproachingRoleAssignmentLimit | Measure-Object).count) Subscriptions approaching Limit ($availableSubscriptionsRoleAssignmentLimits) for RoleAssignment learn
"@)
}
#endregion SUMMARYSubsapproachingLimitsRoleAssignment
@@ -12518,7 +12548,7 @@ tf.init();}}
}
}
else {
- #https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regex.escape
+ #https://learn.microsoft.com/dotnet/api/system.text.regularexpressions.regex.escape
$s1 = $altName -replace '.*/providers/'
$rm = $s1 -replace '.*/'
$resourceType = $s1 -replace "/$([System.Text.RegularExpressions.Regex]::Escape($rm))"
diff --git a/pwsh/dev/functions/runInfo.ps1 b/pwsh/dev/functions/runInfo.ps1
index 49da6ac5..4f0bceb2 100644
--- a/pwsh/dev/functions/runInfo.ps1
+++ b/pwsh/dev/functions/runInfo.ps1
@@ -96,7 +96,7 @@ function runInfo {
}
if (-not $NoAADGroupsResolveMembers) {
- Write-Host " AAD Groups resolve members enabled (honors parameter -DoNotShowRoleAssignmentsUserData) - use parameter: '-NoAADGroupsResolveMembers' to disable resolving AAD Group memberships" -ForegroundColor Yellow
+ Write-Host " Microsoft Entra groups resolve members enabled (honors parameter -DoNotShowRoleAssignmentsUserData) - use parameter: '-NoAADGroupsResolveMembers' to disable resolving group memberships" -ForegroundColor Yellow
$script:paramsUsed += 'NoAADGroupsResolveMembers: false
'
if ($AADGroupMembersLimit -eq 500) {
Write-Host " AADGroupMembersLimit = $AADGroupMembersLimit" -ForegroundColor Yellow
@@ -108,7 +108,7 @@ function runInfo {
}
}
else {
- Write-Host " AAD Groups resolve members disabled (-NoAADGroupsResolveMembers = $($NoAADGroupsResolveMembers))" -ForegroundColor Green
+ Write-Host " Microsoft Entra groups resolve members disabled (-NoAADGroupsResolveMembers = $($NoAADGroupsResolveMembers))" -ForegroundColor Green
$script:paramsUsed += 'NoAADGroupsResolveMembers: true
'
}
diff --git a/setup.md b/setup.md
index 97f4d6c3..cca9fa5a 100644
--- a/setup.md
+++ b/setup.md
@@ -1,637 +1,39 @@
-# Azure Governance Visualizer aka AzGovViz - Setup
+# Azure Governance Visualizer (AzGovViz) deployment guide
-This guide will help you to setup and run AzGovViz
+Follow these steps to deploy the Azure Governance Visualizer. There are three sets of instructions depending on where you wish to execute it. Supported paths are:
-* Abbreviations:
- * Azure DevOps - AzDO
-# Table of content
-* [Azure Governance Visualizer aka AzGovViz - Setup](#azure-governance-visualizer-aka-azgovviz---setup)
-* [Table of content](#table-of-content)
-* [Azure Governance Visualizer from Accelerator](#azure-governance-visualizer-from-accelerator)
-* [Azure Governance Visualizer from Console](#azure-governance-visualizer-from-console)
- * [Grant permissions in Azure](#grant-permissions-in-azure)
- * [Execution options](#execution-options)
- * [Option 1 - Execute as a Tenant Member User](#option-1---execute-as-a-tenant-member-user)
- * [Option 2 - Execute as a Tenant Guest User](#option-2---execute-as-a-tenant-guest-user)
- * [Assign Microsoft Entra ID Role - Directory readers](#assign-microsoft-entra-id-role---directory-readers)
- * [Option 3 - Execute as Service Principal](#option-3---execute-as-service-principal)
- * [Grant API permissions](#grant-api-permissions)
- * [Clone the Azure Governance Visualizer repository](#clone-the-azure-governance-visualizer-repository)
- * [Run Azure Governance Visualizer from Console](#run-azure-governance-visualizer-from-console)
- * [PowerShell \& Azure PowerShell modules](#powershell--azure-powershell-modules)
- * [Connecting to Azure as User (Member or Guest)](#connecting-to-azure-as-user-member-or-guest)
- * [Connecting to Azure using Service Principal](#connecting-to-azure-using-service-principal)
- * [Run Azure Governance Visualizer](#run-azure-governance-visualizer)
-* [Azure Governance Visualizer in Azure DevOps](#azure-governance-visualizer-in-azure-devops)
- * [Create AzDO Project](#create-azdo-project)
- * [Import Azure Governance Visualizer GitHub repository](#import-azure-governance-visualizer-github-repository)
- * [Create AzDO Service Connection](#create-azdo-service-connection)
- * [Create AzDO Service Connection - Option 1 - Create Service Connections Service Principal in the Azure Portal](#create-azdo-service-connection---option-1---create-service-connections-service-principal-in-the-azure-portal)
- * [AzDO supports Open ID Connect - OIDC](#azdo-supports-open-id-connect---oidc)
- * [Azure Portal](#azure-portal)
- * [Azure DevOps](#azure-devops)
- * [Create AzDO Service Connection - Option 2 - Create Service Connection in AzDO](#create-azdo-service-connection---option-2---create-service-connection-in-azdo)
- * [Grant permissions in Azure](#grant-permissions-in-azure-1)
- * [Grant permissions in Microsoft Entra ID](#grant-permissions-in-microsoft-entra-id)
- * [API permissions](#api-permissions)
- * [Grant permissions on Azure Governance Visualizer AzDO repository](#grant-permissions-on-azure-governance-visualizer-azdo-repository)
- * [OPTION 1 (legacy) - Edit AzDO YAML file (.pipelines folder)](#option-1-legacy---edit-azdo-yaml-file-pipelines-folder)
- * [OPTION 1 (legacy) - Create AzDO Pipeline (.pipelines folder)](#option-1-legacy---create-azdo-pipeline-pipelines-folder)
- * [OPTION 2 (new) - Edit AzDO Variables YAML file (.azuredevops folder)](#option-2-new---edit-azdo-variables-yaml-file-azuredevops-folder)
- * [OPTION 2 (new) Create AzDO Pipeline (.azuredevops folder)](#option-2-new-create-azdo-pipeline-azuredevops-folder)
- * [Run the AzDO Pipeline](#run-the-azdo-pipeline)
- * [Create AzDO Wiki (WikiAsCode)](#create-azdo-wiki-wikiascode)
-* [Azure Governance Visualizer in GitHub Actions](#azure-governance-visualizer-in-github-actions)
- * [Create GitHub repository](#create-github-repository)
- * [Import Code](#import-code)
- * [Azure Governance Visualizer YAML](#azure-governance-visualizer-yaml)
- * [Store the credentials in GitHub (Azure Governance Visualizer YAML)](#store-the-credentials-in-github-azure-governance-visualizer-yaml)
- * [Workflow permissions](#workflow-permissions)
- * [Edit the workflow YAML file (Azure Governance Visualizer YAML)](#edit-the-workflow-yaml-file-azure-governance-visualizer-yaml)
- * [Run Azure Governance Visualizer in GitHub Actions (Azure Governance Visualizer YAML)](#run-azure-governance-visualizer-in-github-actions-azure-governance-visualizer-yaml)
- * [Azure Governance Visualizer OIDC YAML](#azure-governance-visualizer-oidc-yaml)
- * [Store the credentials in GitHub (Azure Governance Visualizer OIDC YAML)](#store-the-credentials-in-github-azure-governance-visualizer-oidc-yaml)
- * [Workflow permissions](#workflow-permissions-1)
- * [Edit the workflow YAML file (Azure Governance Visualizer OIDC YAML)](#edit-the-workflow-yaml-file-azure-governance-visualizer-oidc-yaml)
- * [Run Azure Governance Visualizer in GitHub Actions (Azure Governance Visualizer OIDC YAML)](#run-azure-governance-visualizer-in-github-actions-azure-governance-visualizer-oidc-yaml)
-* [Azure Governance Visualizer GitHub Codespaces](#azure-governance-visualizer-github-codespaces)
-* [Optional Publishing the Azure Governance Visualizer HTML to a Azure Web App](#optional-publishing-the-azure-governance-visualizer-html-to-a-azure-web-app)
- * [Prerequisites](#prerequisites)
- * [Azure DevOps](#azure-devops-1)
- * [GitHub Actions](#github-actions)
+- Running it ad-hoc from a workstation console or dev container
+- Running it from Azure DevOps
+- Running it from GitHub
+- Optional Publishing the Azure Governance Visualizer HTML to a Azure Web App
-# Azure Governance Visualizer from Accelerator
+No matter which of the three you choose, they all evaluate the same governance concerns and produce the same reporting results, just the execution and reporting environment is distinct. Use whichever environment is best suited for your situation.
-* The [Azure Governance Visualizer Accelerator](https://github.com/Azure/Azure-Governance-Visualizer-Accelerator) provides an easy and fast deployment process that automates the creation and publishing of AzGovViz to an Azure Web Application and provides automation to configuring the pre-requisites for AzGovViz.
+## Prerequisites
-# Azure Governance Visualizer from Console
+- Your user must have '**Microsoft.Authorization/roleAssignments/write**' permissions on the target management group scope (such as the built-in Azure RBAC role '**User Access Administrator**' or '**Owner**'). This is required to make the required permission changes. If you cannot do this yourself, follow these instructions along with someone who can.
+- To grant Microsoft Graph API permissions and grant admin consent for the Microsoft Entra directory, you must yourself have or work with someone that has the '**Privileged Role Administrator**' or '**Global Administrator**' role assigned in Microsoft Entra ID. (See [Assign Microsoft Entra roles to users](https://learn.microsoft.com/entra/identity/role-based-access-control/manage-roles-portal).)
-## Grant permissions in Azure
+## Set up and run Azure Governance Visualizer from the console
-* Requirements
- * To assign roles, you must have '__Microsoft.Authorization/roleAssignments/write__' permissions on the target Management Group scope (such as the built-in RBAC Role '__User Access Administrator__' or '__Owner__')
+To set up local execution of the Azure Governance Visualizer without involving automation from Azure pipelines or GitHub actions. This solution is good for proof of value exploration, local development, etc. It's encouraged that you use Azure DevOps pipelines or GitHub actions for a formal deployment.
-Create a '__Reader__' RBAC Role assignment on the target Management Group scope for the identity that shall run Azure Governance Visualizer
+:arrow_right: Follow the instructions to [Configure and run from the console](./setup/console.md).
-* PowerShell
+## Set up and run Azure Governance Visualizer in Azure DevOps
-```powershell
-$objectId = ""
-$role = "Reader"
-$managementGroupId = ""
+The Azure Governance Visualizer lifecycle can be hosted out of Azure DevOps. This includes automated pipelines, service connections, and even automated wiki generations. This path also optionally supports publishing the generated HTML report to Azure Web Apps.
-New-AzRoleAssignment `
--ObjectId $objectId `
--RoleDefinitionName $role `
--Scope /providers/Microsoft.Management/managementGroups/$managementGroupId
-```
+:arrow_right: Follow the instructions to [Configure and run from Azure DevOps](./setup/azure-devops.md).
-* Azure Portal
-[Assign Azure roles using the Azure portal](https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal)
+## Set up and run Azure Governance Visualizer in GitHub
-## Execution options
+To set up the Azure Governance Visualizer lifecycle, including automated actions, service connections, and GitHub Codespaces. This path also optionally supports publishing the generated HTML report to Azure Web Apps.
-### Option 1 - Execute as a Tenant Member User
+:arrow_right: Follow the instructions to [Configure and run from GitHub](./setup/github.md).
-Proceed with step [__Clone the Azure Governance Visualizer repository__](#clone-the-azure-governance-visualizer-repository)
+## Optional Publishing the Azure Governance Visualizer HTML to a Azure Web App
-### Option 2 - Execute as a Tenant Guest User
+Set up the Azure Web App, so that with each execution of the Azure Governance Visualizer the latest HTML file gets published to an Azure Web App. Supported setups are Azure DevOps and GitHub Actions.
-If the tenant is hardened (Microsoft Entra ID External Identities / Guest user access = most restrictive) then Guest User must be assigned the Microsoft Entra ID Role '__Directory readers__'
-
-💡 [Compare member and guest default permissions](https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/active-directory/fundamentals/users-default-permissions.md#compare-member-and-guest-default-permissions)
-
-💡 [Restrict Guest permissions](https://docs.microsoft.com/en-us/azure/active-directory/enterprise-users/users-restrict-guest-permissions)
-
-#### Assign Microsoft Entra ID Role - Directory readers
-
-* Requirements
- * To assign roles, you must have '__Privileged Role Administrator__' or '__Global Administrator__' role assigned [Assign Azure AD roles to users](https://docs.microsoft.com/en-us/azure/active-directory/roles/manage-roles-portal)
-
-Assign the Microsoft Entra ID Role '__Directory Reader__' for the Guest User that shall run Azure Governance Visualizer (work with the Guest User´s display name)
-
-* Azure Portal
- * [Assign a role](https://docs.microsoft.com/en-us/azure/active-directory/roles/manage-roles-portal#assign-a-role)
-
-Proceed with step [__Clone the Azure Governance Visualizer repository__](#clone-the-azure-governance-visualizer-repository)
-
-### Option 3 - Execute as Service Principal
-
-A Service Principal by default has no read permissions on Users, Groups and Service Principals, therefore we need to grant additional permissions in Microsoft Entra ID
-
-#### Grant API permissions
-
-* Requirements
- * To grant API permissions and grant admin consent for the directory, you must have '__Privileged Role Administrator__' or '__Global Administrator__' role assigned [Assign Azure AD roles to users](https://docs.microsoft.com/en-us/azure/active-directory/roles/manage-roles-portal)
-
-Grant API permissions for the Service Principal´s Application
-
-* Navigate to 'Microsoft Entra ID (AAD)'
-* Click on '__App registrations__'
-* Search for the Application that we created earlier and click on it
-* Under '__Manage__' click on '__API permissions__'
- * Click on '__Add a permissions__'
- * Click on '__Microsoft Graph__'
- * Click on '__Application permissions__'
- * Select the following set of permissions and click '__Add permissions__'
- * __Application / Application.Read.All__
- * __Group / Group.Read.All__
- * __User / User.Read.All__
- * __PrivilegedAccess / PrivilegedAccess.Read.AzureResources__
- * Click on 'Add a permissions'
- * Back in the main '__API permissions__' menu you will find permissions with status 'Not granted for...'. Click on '__Grant admin consent for _TenantName___' and confirm by click on '__Yes__'
- * Now you will find the permissions with status '__Granted for _TenantName___'
-
-Permissions in Microsoft Entra ID (AAD) for App registration:
-![alt text](img/aadpermissionsportal_4.jpg "Permissions in Microsoft Entra ID (AAD)")
-
-Proceed with step [__Clone the Azure Governance Visualizer repository__](#clone-the-azure-governance-visualizer-repository)
-
-## Clone the Azure Governance Visualizer repository
-
-* Requirements
- * To clone the Azure Governance Visualizer GitHub repository you need to have GIT installed
- * Install Git: [https://git-scm.com/download/win](https://git-scm.com/download/win)
-
-* PowerShell
-
-```powershell
-Set-Location "c:\Git"
-git clone "https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting.git"
-```
-
-Proceed with step [__Run Azure Governance Visualizer from Console__](#run-azure-governance-visualizer-from-console)
-
-## Run Azure Governance Visualizer from Console
-
-### PowerShell & Azure PowerShell modules
-
-* Requirements
- * Requires PowerShell 7 (minimum supported version 7.0.3)
- * [Get PowerShell](https://github.com/PowerShell/PowerShell#get-powershell)
- * [Installing PowerShell on Windows](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows)
- * [Installing PowerShell on Linux](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-linux)
- * Requires PowerShell Az Modules
- * Az.Accounts
- * [Install the Azure Az PowerShell module](https://docs.microsoft.com/en-us/powershell/azure/install-az-ps)
- * AzAPICall
- * Running from Console the script will prompt you to confirm installation of the required AzAPICall module version
- * AzAPICall resources:
- * [![PowerShell Gallery Version (including pre-releases)](https://img.shields.io/powershellgallery/v/AzAPICall?include_prereleases&label=PowerShell%20Gallery)](https://www.powershellgallery.com/packages/AzAPICall)
- * [GitHub Repository](https://aka.ms/AzAPICall)
-
-### Connecting to Azure as User (Member or Guest)
-
-* PowerShell
-
-```powershell
-Connect-AzAccount -TenantId -UseDeviceAuthentication
-```
-
-### Connecting to Azure using Service Principal
-
-Have the '__Application (client) ID__' of the App registration OR '__Application ID__' of the Service Principal (Enterprise Application) and the secret of the App registration at hand
-
-* PowerShell
-
-```powershell
-$pscredential = Get-Credential
-Connect-AzAccount -ServicePrincipal -TenantId -Credential $pscredential
-```
-
-User: Enter '__Application (client) ID__' of the App registration OR '__Application ID__' of the Service Principal (Enterprise Application)
-Password for user \: Enter App registration´s secret
-
-### Run Azure Governance Visualizer
-
-Familiarize yourself with the available [parameters](https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting#parameters) for Azure Governance Visualizer
-
-* PowerShell
-
-```powershell
-c:\Git\Azure-MG-Sub-Governance-Reporting\pwsh\AzGovVizParallel.ps1 -ManagementGroupId
-```
-
-Note if not using the `-OutputPath` parameter, all outputs will be created in the current directory. The following example will create the outputs in directory c:\AzGovViz-Output (directory must exist)
-
-* PowerShell
-
-```powershell
-c:\Git\Azure-MG-Sub-Governance-Reporting\pwsh\AzGovVizParallel.ps1 -ManagementGroupId -OutputPath "c:\AzGovViz-Output"
-```
-
-# Azure Governance Visualizer in Azure DevOps
-
-## Create AzDO Project
-
-[Create a project](https://docs.microsoft.com/en-us/azure/devops/organizations/projects/create-project?view=azure-devops&tabs=preview-page#create-a-project)
-
-## Import Azure Governance Visualizer GitHub repository
-
-Azure Governance Visualizer Clone URL: `https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting.git`
-
-[Import into a new repo](https://docs.microsoft.com/en-us/azure/devops/repos/git/import-git-repository?view=azure-devops#import-into-a-new-repo)
-
-Note: the Azure Governance Visualizer GitHub repository is public - no authorization required
-
-## Create AzDO Service Connection
-
-For the pipeline to authenticate and connect to Azure we need to create an AzDO Service Connection which basically is a Service Principal (Application)
-There are two options to create the Service Connection:
-
-* Options
- * __Option 1__ Create Service Connection´s Service Principal in the Azure Portal
- * __Option 2__ Create Service Connection in AzDO
-
-### Create AzDO Service Connection - Option 1 - Create Service Connections Service Principal in the Azure Portal
-
-#### AzDO supports Open ID Connect - OIDC
-
-Using OIDC we will not have the requirement to create a secret, nore store it in AzDO - awesome :)
-
-Quick guide for an app registration:
-
-__AzDO:__
-
-* Click on '__Project settings__' (located on the bottom left)
-* Under '__Pipelines__' click on '__Service Connections__'
-* Click on '__New service connection__' and select the connection/service type '__Azure Resource Manager__' and click '__Next__'
-* Select Authentication method __Workload Identity federation (manual)__
-
-![alt text](img/azdo_oidc_0.jpg "Microsoft Entra ID (AAD) Federated credentials")
-
-Copy away:
-* value of __Issuer__
-* value of __Subject identifier__
-
-![alt text](img/azdo_oidc_1.jpg "Microsoft Entra ID (AAD) Federated credentials; issuer, subject identifier")
-
-__Microsoft Entra ID (AAD):__
-
-* In the Azure Portal navigate to 'Microsoft Entra ID (AAD)'
-* Click on '__App registrations__'
-* Click on '__New registration__'
-* Name your application (e.g. 'AzureGovernanceVisualizer_SC')
-* Click '__Register__'
-* Your App registration has been created
-* Under '__Manage__' click on '__Certificates & Secrets__'
-* Click on '__Federated credentials__' and '__Add credential__'
-
-![alt text](img/azdo_aad_oidc_0.jpg "Microsoft Entra ID (AAD) Federated credentials")
-
-Paste the just copied off
-* value for __Issuer__
-* value for __Subject identifier__
-
-![alt text](img/azdo_aad_oidc_1.jpg "Microsoft Entra ID (AAD) Federated credentials; issuer, subject identifier")
-
-#### Azure Portal
-* Navigate to 'Microsoft Entra ID (AAD)'
-* Click on '__App registrations__'
-* Click on '__New registration__'
-* Name your application (e.g. 'AzureGovernanceVisualizer_SC')
-* Click '__Register__'
-* Your App registration has been created, in the '__Overview__' copy the '__Application (client) ID__' as we will need it later to setup the Service Connection in AzDO
-* Under '__Manage__' click on '__Certificates & Secrets__'
-* Click on '__New client secret__'
-* Provide a good description and choose the expiry time based on your need and click '__Add__'
-* A new client secret has been created, copy the secret´s value as we will need it later to setup the Service Connection in AzDO
-
-__Note:__ if you do not assign the RBAC 'Reader' role to the Management group at this stage then the '__Verify__' step in [Azure DevOps](#azure-devops) will fail.
-* In the portal proceed to '__Management Groups__', select the scope at which Azure Governance Visualizer will run, usually __Tenant Root Group__
-* Go to '__Access Control (IAM)__', '__Grant Access__' and '__Add Role Assignment__', select '__Reader__', click '__Next__'
-* Now '__Select Member__', this will be the name of the Application you created above (e.g. 'AzureGovernanceVisualizer_SC').
-* Select '__Next__', '__Review + Assign__'
-
-#### Azure DevOps
-* Click on '__Project settings__' (located on the bottom left)
-* Under '__Pipelines__' click on '__Service Connections__'
-* Click on '__New service connection__' and select the connection/service type '__Azure Resource Manager__' and click '__Next__'
-* For the authentication method select '__Service principal (manual)__' and click '__Next__'
-* For the '__Scope level__' select '__Management Group__'
- * In the field '__Management Group Id__' enter the target Management Group Id
- * In the field '__Management Group Name__' enter the target Management Group Name
-* Under '__Authentication__' in the field '__Service Principal Id__' enter the '__Application (client) ID__' that you copied away earlier
-* For the '__Credential__' select '__Service principal key__', in the field '__Service principal key__' enter the secret that you copied away earlier
-* For '__Tenant ID__' enter your Tenant Id
-* Click on '__Verify__'
-* Under '__Details__' provide your Service Connection with a name and copy away the name as we will need that later when editing the Pipeline YAML file
-* For '__Security__' leave the 'Grant access permissions to all pipelines' option checked (optional)
-* Click on '__Verify and save__'
-
-### Create AzDO Service Connection - Option 2 - Create Service Connection in AzDO
-
-* Click on '__Project settings__' (located on the bottom left)
-* Under '__Pipelines__' click on '__Service connections__'
-* Click on '__New service connection__' and select the connection/service type '__Azure Resource Manager__' and click '__Next__'
-* For the authentication method select '__Service principal (automatic)__' and click '__Next__'
-* For the '__Scope level__' select '__Management Group__', in the Management Group dropdown select the target Management Group (here the Management Group´s display names will be shown), in the '__Details__' section apply a Service Connection name and optional give it a description and click '__Save__'
-* A new window will open, authenticate with your administrative account
-* Now the Service Connection has been created
-
-__Important!__ In Azure on the target Management Group scope an '__Owner__' RBAC Role assignment for the Service Connection´s Service Principal has been created automatically (we do however only require a '__Reader__' RBAC Role assignment! we will take corrective action in the next steps)
-
-## Grant permissions in Azure
-
-* Requirements
- * To assign roles, you must have '__Microsoft.Authorization/roleAssignments/write__' permissions on the target Management Group scope (such as the built-in RBAC Role '__User Access Administrator__' or '__Owner__')
-
-Create a '__Reader__' RBAC Role assignment on the target Management Group scope for the AzDO Service Connection´s Service Principal
-
-* PowerShell
-
-```powershell
-$objectId = ""
-$role = "Reader"
-$managementGroupId = ""
-
-New-AzRoleAssignment `
--ObjectId $objectId `
--RoleDefinitionName $role `
--Scope /providers/Microsoft.Management/managementGroups/$managementGroupId
-```
-
-* Azure Portal
-[Assign Azure roles using the Azure portal](https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal)
-
-__Important!__ If you have created the AzDO Service Connection in AzDO (Option 2) then you SHOULD remove the automatically created '__Owner__' RBAC Role assignment for the AzDO Service Connection´s Service Principal from the target Management Group
-
-## Grant permissions in Microsoft Entra ID
-
-### API permissions
-
-* Requirements
- * To grant API permissions and grant admin consent for the directory, you must have '__Privileged Role Administrator__' or '__Global Administrator__' role assigned ([Assign Azure AD roles to users](https://docs.microsoft.com/en-us/azure/active-directory/roles/manage-roles-portal))
-
-Grant API permissions for the Service Principal´s Application that we created earlier
-
-* Navigate to 'Microsoft Entra ID (AAD)'
-* Click on '__App registrations__'
-* Search for the Application that we created earlier and click on it
-* Under '__Manage__' click on '__API permissions__'
- * Click on '__Add a permissions__'
- * Click on '__Microsoft Graph__'
- * Click on '__Application permissions__'
- * Select the following set of permissions and click '__Add permissions__'
- * __Application / Application.Read.All__
- * __Group / Group.Read.All__
- * __User / User.Read.All__
- * __PrivilegedAccess / PrivilegedAccess.Read.AzureResources__
- * Click on 'Add a permissions'
- * Back in the main '__API permissions__' menu you will find the permissions with status 'Not granted for...'. Click on '__Grant admin consent for _TenantName___' and confirm by click on '__Yes__'
- * Now you will find the permissions with status '__Granted for _TenantName___'
-
-Permissions in Microsoft Entra ID (AAD) for App registration:
-![alt text](img/aadpermissionsportal_4.jpg "Permissions in Microsoft Entra ID (AAD)")
-
-## Grant permissions on Azure Governance Visualizer AzDO repository
-
-When the AzDO pipeline executes the Azure Governance Visualizer script the outputs should be pushed back to the Azure Governance Visualizer AzDO repository, in order to do this we need to grant the AzDO Project´s Build Service account with 'Contribute' permissions on the repository
-
-* Grant permissions on the Azure Governance Visualizer AzDO repository
- * In AzDO click on '__Project settings__' (located on the bottom left), under '__Repos__' open the '__Repositories__' page
- * Click on the Azure Governance Visualizer AzDO Repository and select the tab '__Security__'
- * On the right side search for the Build Service account
- __%Project name% Build Service (%Organization name%)__ and grant it with '__Contribute__' permissions by selecting '__Allow__' (no save button available)
-
-## OPTION 1 (legacy) - Edit AzDO YAML file (.pipelines folder)
-
-* Click on '__Repos__'
-* Navigate to the Azure Governance Visualizer Repository
-* In the folder '__pipeline__' click on '__AzGovViz.yml__' and click '__Edit__'
-* Under the variables section
- * Enter the Service Connection name that you copied earlier (ServiceConnection)
- * Enter the Management Group Id (ManagementGroupId)
-* Click '__Commit__'
-
-## OPTION 1 (legacy) - Create AzDO Pipeline (.pipelines folder)
-
-* Click on '__Pipelines__'
-* Click on '__New pipeline__'
-* Select '__Azure Repos Git__'
-* Select the Azure Governance Visualizer repository
-* Click on '__Existing Azure Pipelines YAML file__'
-* Under '__Path__' select '__/.pipelines/AzGovViz.yml__' (the YAML file we edited earlier)
-* Click ' __Save__'
-
-## OPTION 2 (new) - Edit AzDO Variables YAML file (.azuredevops folder)
-
->For the '__parameters__' and '__variables__' sections, details about each parameter or variable is documented inline.
-
-* Click on '__Repos__'
-* Navigate to the Azure Governance Visualizer repository
-* In the folder '__/.azuredevops/pipelines__' click on '__AzGovViz.variables.yml__' and click '__Edit__'
-* If needed, modify the '__parameters__' section:
- * For more information about [parameters](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/runtime-parameters)
- * [Optional] Update the '__ExcludedResourceTypesDiagnosticsCapableParameters__'
- * [Optional] Update the '__SubscriptionQuotaIdWhitelistParameters__'
-* Update the '__Required Variables__' section:
- * Replace `` with the Service connection name you copied earlier (ServiceConnection)
- * Replace `` with the Management Group Id (ManagementGroupId)
-* If needed, update the '__Default Variables__' section
-* If needed, update the '__Optional Variables__' section
-
-### OPTION 2 (new) Create AzDO Pipeline (.azuredevops folder)
-
-* Click on '__Pipelines__'
-* Click on '__New pipeline__'
-* Select '__Azure Repos Git__'
-* Select the Azure Governance Visualizer repository
-* Click on '__Existing Azure Pipelines YAML file__'
-* Under '__Path__' select '__/.azuredevops/pipelines/AzGovViz.pipeline.yml__'
-* Click ' __Save__'
-
-## Run the AzDO Pipeline
-
-* Click on '__Pipelines__'
-* Select the Azure Governance Visualizer pipeline
-* Click '__Run pipeline__'
-
-Note: Before the pipeline kicks off it may require you to approve the run (only first time run)
-
-## Create AzDO Wiki (WikiAsCode)
-
-Once the pipeline has executed successfully we can setup our Wiki (WikiAsCode)
-
-* Click on '__Overview__'
-* Click on '__Wiki__'
-* Click on '__Publish code as wiki__'
-* Select the Azure Governance Visualizer repository
-* Select the folder '__wiki__' and click '__OK__'
-* Enter a name for the Wiki
-* Click '__Publish__'
-
-# Azure Governance Visualizer in GitHub Actions
-
-## Create GitHub repository
-
-Create a 'private' repository
-
-## Import Code
-
-Click on 'Import code'
-
-Use 'https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting.git' as clone URL
-
-Click on 'Begin import'
-
-Navigate to your newly created repository
-In the folder `./github/workflows` two worklows are available:
-1. [AzGovViz.yml](#azgovviz-yaml)
-Use this workflow if you want to store your Application (App registration) secret in GitHub
-
-2. [AzGovViz_OIDC.yml](#azgovviz-oidc-yaml)
-Use this workflow if you want leverage the [OIDC (Open ID Connect) feature](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure) - no secret stored in GitHub
-
-## Azure Governance Visualizer YAML
-
-For the GitHub Actiom to authenticate and connect to Azure we need to create Service Principal (Application)
-
-In the Azure Portal navigate to 'Microsoft Entra ID (AAD)'
-* Click on '__App registrations__'
-* Click on '__New registration__'
-* Name your application (e.g. 'AzureGovernanceVisualizer_SC')
-* Click '__Register__'
-* Your App registration has been created, in the '__Overview__' copy the '__Application (client) ID__' as we will need it later to setup the secrets in GitHub
-* Under '__Manage__' click on '__Certificates & Secrets__'
-* Click on '__New client secret__'
-* Provide a good description and choose the expiry time based on your need and click '__Add__'
-* A new client secret has been created, copy the secret´s value as we will need it later to setup the secrets in GitHub
-
-### Store the credentials in GitHub (Azure Governance Visualizer YAML)
-
-In GitHub navigate to 'Settings'
-* Click on 'Secrets'
-* Click on 'Actions'
-* Click 'New repository secret'
- * Name: CREDS
- * Value:
-```
-{
- "tenantId": "",
- "subscriptionId": "",
- "clientId": "",
- "clientSecret": ""
-}
-```
-
-### Workflow permissions
-
-In GitHub navigate to 'Settings'
-* Click on 'Actions'
-* Click on 'General'
-* Under 'Workflow permissions' select '__Read and write permissions__'
-* Click 'Save'
-
-### Edit the workflow YAML file (Azure Governance Visualizer YAML)
-
-* In the folder `./github/workflows` edit the YAML file `AzGovViz.yml`
-* In the `env` section enter you Management Group ID
-* If you want to continuously run Azure Governance Visualizer then enable the `schedule` in the `on` section
-
-### Run Azure Governance Visualizer in GitHub Actions (Azure Governance Visualizer YAML)
-
-In GitHub navigate to 'Actions'
-* Click 'Enable GitHub Actions on this repository'
-* Select the Azure Governance Visualizer workflow
-* Click 'Run workflow'
-
-## Azure Governance Visualizer OIDC YAML
-
-For the GitHub Actiom to authenticate and connect to Azure we need to create Service Principal (Application). Using OIDC we will not have the requirement to create a secret, nore store it in GitHub - awesome :)
-
-* Navigate to 'Microsoft Entra ID (AAD)'
-* Click on '__App registrations__'
-* Click on '__New registration__'
-* Name your application (e.g. 'AzureGovernanceVisualizer_SC')
-* Click '__Register__'
-* Your App registration has been created, in the '__Overview__' copy the '__Application (client) ID__' as we will need it later to setup the secrets in GitHub
-* Under '__Manage__' click on '__Certificates & Secrets__'
-* Click on '__Federated credentials__'
-* Click 'Add credential'
-* Select Federation credential scenario 'GitHub Actions deploying Azure Resources'
-* Fill the field 'Organization' with your GitHub Organization name
-* Fill the field 'Repository' with your GitHub repository name
-* For the entity type select 'Branch'
-* Fill the field 'GitHub branch name' with your branch name (default is 'master' if you imported the Azure Governance Visualizer repository)
-* Fill the field 'Name' with a name (e.g. AzureGovernanceVisualizer_GitHub_Actions)
-* Click 'Add'
-
-### Store the credentials in GitHub (Azure Governance Visualizer OIDC YAML)
-
-In GitHub navigate to 'Settings'
-* Click on 'Secrets'
-* Click on 'Actions'
-* Click 'New repository secret'
-* Create the following three secrets:
- * Name: CLIENT_ID
- Value: `Application (client) ID`
- * Name: TENANT_ID
- Value: `Tenant ID`
- * Name: SUBSCRIPTION_ID
- Value: `Subscription ID`
-
-### Workflow permissions
-
-In GitHub navigate to 'Settings'
-* Click on 'Actions'
-* Click on 'General'
-* Under 'Workflow permissions' select '__Read and write permissions__'
-* Click 'Save'
-
-### Edit the workflow YAML file (Azure Governance Visualizer OIDC YAML)
-
-* In the folder `./github/workflows` edit the YAML file `AzGovViz_OIDC.yml`
-* In the `env` section enter you Management Group ID
-* If you want to continuously run Azure Governance Visualizer then enable the `schedule` in the `on` section
-
-### Run Azure Governance Visualizer in GitHub Actions (Azure Governance Visualizer OIDC YAML)
-
-In GitHub navigate to 'Actions'
-* Click 'Enable GitHub Actions on this repository'
-* Select the AzGovViz_OIDC workflow
-* Click 'Run workflow'
-
-# Azure Governance Visualizer GitHub Codespaces
-
-Note: Codespaces is available for organizations using GitHub Team or GitHub Enterprise Cloud. [Quickstart for Codespaces](https://docs.github.com/en/codespaces/getting-started/quickstart)
-
-![alt text](img/codespaces0.png "Azure Governance Visualizer GitHub Codespaces")
-
-![alt text](img/codespaces1.png "Azure Governance Visualizer GitHub Codespaces")
-
-![alt text](img/codespaces2.png "Azure Governance Visualizer GitHub Codespaces")
-
-![alt text](img/codespaces3.png "Azure Governance Visualizer GitHub Codespaces")
-
-![alt text](img/codespaces4.png "Azure Governance Visualizer GitHub Codespaces")
-
-# Optional Publishing the Azure Governance Visualizer HTML to a Azure Web App
-
-There are instances where you may want to publish the HTML output to a webapp so that anybody in the business can see up to date status of the Azure governance.
-
-There are a few models to do this, the option below is one way to get you started.
-
-## Prerequisites
-* Deploy a simple webapp on Azure. This can be the smallest SKU or a FREE SKU. It doesn't matter whether you choose Windows or Linux as the platform
-![alt text](img/webapp_create.png "Web App Create")
-* Step through the configuration. I typically use the Code for the publish and then select the Runtime stack that you standardize on
-![alt text](img/webapp_configure.png "Web App Configure")
-* No need to configure anything, unless your organization policies require you to do so
-NOTE: it is a good practice to tag your resource for operational and finance reasons
-* In the webapp _Configuration_ add the name of the HTML output file to the _Default Documents_
-![alt text](img/webapp_defaultdocs.png "Web App Default documents")
-* Make sure to configure Authentication!
-![alt text](img/webapp_authentication.png "Web App Authentication")
-
-## Azure DevOps
-
-* Assign the Azure DevOps Service Connection´s Service Principal with RBAC Role __Website Contributor__ on the Azure Web App
-* Edit the `.azuredevops/AzGovViz.variables.yml` file
-![alt text](img/webapp_AzDO_yml.png "Azure DevOps YAML variables")
-
-## GitHub Actions
-
-* Assign the Service Principal used in GitHub with RBAC Role __Website Contributor__ on the Azure Web App
-* Edit the `.github/workflows/AzGovViz_OIDC.yml` or `.github/workflows/AzGovViz.yml` file
-![alt text](img/webapp_GitHub_yml.png "GitHub YAML variables")
+:arrow_right: Follow the instructions to [Optional Publishing the Azure Governance Visualizer HTML to a Azure Web App](./setup/azure-web-app.md).
diff --git a/setup/azure-devops.md b/setup/azure-devops.md
new file mode 100644
index 00000000..732fef8f
--- /dev/null
+++ b/setup/azure-devops.md
@@ -0,0 +1,213 @@
+# Configure and run Azure Governance Visualizer from Azure DevOps
+
+Azure DevOps can be used to orchestrate regular execution of Azure Governance Visualizer against your target management group. This allows headless, automated execution along with the ability to set least privileges on the executing account. It uses Azure pipelines as the workflow orchestrator. These instructions will get you up and running from Azure DevOps.
+
+## Prerequisites
+
+- An Azure DevOps account in which you have enough permissions to create a new project.
+
+## 1. Create an Azure DevOps project
+
+[Create an Azure DevOps project](https://learn.microsoft.com/azure/devops/organizations/projects/create-project?view=azure-devops&tabs=browser#create-a-project) to use as the home for the pipelines, artifacts, and service connections.
+
+While you could use an existing one, these instructions are written with a new project in mind. Adjust as needed to use an existing project.
+
+## 2. Import Azure Governance Visualizer GitHub repository
+
+Azure Governance Visualizer clone URL: `https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting.git`
+
+Follow the instructions on Microsoft Learn to [Import this repo into a new repo](https://learn.microsoft.com/azure/devops/repos/git/import-git-repository?view=azure-devops#import-into-a-new-repo). The Azure Governance Visualizer GitHub repository is public, so no authorization is required.
+
+## 3. Create Azure DevOps Pipeline service connection
+
+For pipelines to authenticate and connect to Azure you need to create an Azure Resource Manager service connection. This will allow the Azure Governance Visualizer scripts to connect to Azure resources under the identity of a properly permissioned service principal.
+
+There are a few options to create the service connection, both will result in least privilege access:
+
+- **Option 1** - [Use workload identity federation](#option-1---use-workload-identity-federation-recommended) _(This is the recommended option.)_
+- **Option 2** - [Create a service principal service connection from Azure DevOps](#option-2---create-a-service-principal-service-connection-from-azure-devops)
+
+### Option 1 - Use workload identity federation (recommended)
+
+This option uses Microsoft Entra workload identity federation to manage a service principal but without the need for you to manage secrets or secret expiration. This is the recommended method.
+
+#### Start the process in Azure DevOps
+
+1. Open your project in [Azure DevOps](http://dev.azure.com/).
+1. Click on '**Project settings**' (located on the bottom left)
+1. Under '**Pipelines**' click on '**Service Connections**'
+1. Click on '**New service connection**' and select the connection/service type '**Azure Resource Manager**' and click '**Next**'
+1. Select Authentication method **Workload Identity federation (manual)**
+
+ ![Microsoft Entra ID Federated credentials](../img/azdo_oidc_0.jpg)
+
+1. Give the connection a meaningful name and description.
+1. For '**Security**' leave the 'Grant access permissions to all pipelines' option checked (optional)
+1. Copy the values of the following results, as you'll need those in the next set of steps for Microsoft Entra configuration.
+
+ - **Issuer**
+ - **Subject identifier**
+
+ ![Microsoft Entra ID federated credentials; issuer, subject identifier](../img/azdo_oidc_1.jpg)
+
+#### Continue by creating and configuring an app registration in Microsoft Entra ID
+
+Doing the "manual" path requires jumping over to Microsoft Entra ID to create a service principal before continuing creating the Azure service connection.
+
+1. In a new browser tab, navigate to the [Microsoft Entra admin center](https://entra.microsoft.com/)
+1. Click on '**App registrations**'
+1. Click on '**New registration**'
+1. Name your application (e.g. 'AzureGovernanceVisualizer_SC')
+1. Click '**Register**'
+1. Your App registration has been created
+1. Copy (note) the '**Application (client) ID**', as you'll need it back in Azure DevOps.
+1. Under '**Manage**' click on '**Certificates & Secrets**'
+1. Click on '**Federated credentials**' and '**Add credential**'
+ ![Microsoft Entra ID federated credentials](../img/azdo_aad_oidc_0.jpg)
+1. Paste the values copied in a prior step.
+ - **Issuer**
+ - **Subject identifier**
+
+ ![Microsoft Entra ID (AAD) Federated credentials; issuer, subject identifier](../img/azdo_aad_oidc_1.jpg)
+
+#### Assign least privileges to the new identity from the Azure portal
+
+1. In the [Azure portal](https://portal.azure.com) proceed to '**Management Groups**', select the scope at which Azure Governance Visualizer will run. This is usually the **Tenant Root Group**.
+1. Go to '**Access Control (IAM)**', '**Grant Access**' and '**Add Role Assignment**', select '**Reader**', click '**Next**'
+1. Now '**Select Member**', this will be the name of the Application you created above (e.g. 'AzureGovernanceVisualizer_SC').
+1. Select '**Next**', '**Review + Assign**'
+
+#### Complete the process in Azure DevOps
+
+1. Return to the tab with the "New Azure service connection" workflow in [Azure DevOps](http://dev.azure.com/) and pick up where you left off.
+1. For the '**Scope level**' select '**Management Group**'
+ - In the field '**Management Group Id**' enter the target Azure management group ID
+ - In the field '**Management Group Name**' enter the target Azure management group name
+1. Under '**Authentication**' in the field '**Service Principal Id**' enter the '**Application (client) ID**' that you noted earlier.
+1. For '**Tenant ID**' enter your Microsoft Entra tenant ID.
+1. Click on '**Verify and save**'
+
+### Option 2 - Create a service principal service connection from Azure DevOps
+
+This option uses an automatically created service principal, but requires some authentication tweaks to achieve least privilege. The service principal secret that automatically used in this option will expire in three months, after which you need to refresh the service connection.
+
+1. Open your project in [Azure DevOps](http://dev.azure.com/).
+1. Click on '**Project settings**' (located on the bottom left)
+1. Under '**Pipelines**' click on '**Service connections**'
+1. Click on '**New service connection**' and select the connection/service type '**Azure Resource Manager**' and click '**Next**'
+1. For the authentication method select '**Service principal (automatic)**' and click '**Next**'
+1. For the '**Scope level**' select '**Management Group**', in the Management Group dropdown select the target Management Group (here the Management Group's display names will be shown), in the '**Details**' section apply a Service Connection name and optional give it a description
+1. Click '**Save**'
+1. A new window will open, authenticate with your administrative account
+
+**Important!** In Azure, on the target management group scope an '**Owner**' Azure RBAC role assignment for the service connection's service principal has been created automatically. This is more permissions than necessary, as the Azure Governance Visualizer only requires '**Reader**' role assignment. This will be corrected in the next steps.
+
+#### Adjust permissions on the service principal from Azure
+
+Create a '**Reader**' Azure RBAC role assignment on the target management group scope for the AzDO service connection's service principal.
+
+```powershell
+$objectId = ""
+$managementGroupId = ""
+
+New-AzRoleAssignment `
+-ObjectId $objectId `
+-RoleDefinitionName "Reader" `
+-Scope /providers/Microsoft.Management/managementGroups/$managementGroupId
+
+Remove-AzRoleAssignment `
+-ObjectId $objectId `
+-RoleDefinitionName "Owner" `
+-Scope /providers/Microsoft.Management/managementGroups/$managementGroupId
+```
+
+If you'd like to use the Azure portal instead; follow the instructions on Microsoft Learn to [Assign Azure roles using the Azure portal](https://learn.microsoft.com/azure/role-based-access-control/role-assignments-portal). Grant the service principal the '**Reader**' role on the target management group. Then follow the instructions to [Remove Azure role assignments](https://learn.microsoft.com/azure/role-based-access-control/role-assignments-remove) to remove the automatically assigned '**Owner**' role from the service principal on the target management group.
+
+## 4. Grant Microsoft Graph API permissions in Microsoft Entra ID
+
+The service principal created in the prior step is authorized for Azure resource access, but also must be authorized for Microsoft Entra ID directory querying through Microsoft Graph. The instructions in this step will configure that.
+
+1. Navigate to the [Microsoft Entra admin center](https://entra.microsoft.com/).
+1. Click on '**App registrations**'
+1. Search for the existing application (service principal)
+1. Under '**Manage**' click on '**API permissions**'
+1. Click on '**Add a permissions**'
+1. Click on '**Microsoft Graph**'
+1. Click on '**Application permissions**'
+1. Select the following set of permissions and click '**Add permissions**'
+ - **Application / Application.Read.All**
+ - **Group / Group.Read.All**
+ - **User / User.Read.All**
+ - **PrivilegedAccess / PrivilegedAccess.Read.AzureResources**
+1. Click on 'Add a permissions'
+1. Back in the main '**API permissions**' menu you will find permissions with status 'Not granted for...'. Click on '**Grant admin consent for _TenantName_**' and confirm by click on '**Yes**'
+ - Now you will find the permissions with status '**Granted for _TenantName_**'
+
+Permissions and admin consent granted in Microsoft Entra ID for the service principal (App Registration):
+
+![Permissions in Microsoft Entra ID](../img/aadpermissionsportal_4.jpg)
+
+## 5. Grant permissions on Azure Governance Visualizer Azure DevOps repository
+
+When the Azure pipeline executes the Azure Governance Visualizer script the output from the script should be pushed back to the Azure Governance Visualizer Azure DevOps repository. In order to do this, you need to grant the Azure DevOps project's 'Build Service' account with '**Contribute**' permissions on the repository.
+
+1. Open your project in [Azure DevOps](http://dev.azure.com/).
+1. Click on '**Project settings**' (located on the bottom left)
+1. Under '**Repos**', click '**Repositories**'.
+1. Click on the Azure Governance Visualizer repository and select the tab '**Security**'
+1. On the right side search for the 'Build Service' account **%Project name% Build Service (%Organization name%)** and grant it with '**Contribute**' permissions by selecting '**Allow**' (no save button available)
+
+## 6. Configure parameters in the Azure pipeline YAML file
+
+You'll need to modify the '**AzGovViz.variables.yml**' file to work with the service connection you created and to point to your target management group.
+
+> For the '**parameters**' and '**variables**' sections, details about each parameter or variable is documented inline.
+
+1. Open your project in [Azure DevOps](http://dev.azure.com/).
+1. Click on '**Repos**'
+1. Navigate to the Azure Governance Visualizer repository
+1. In the folder '**/.azuredevops/pipelines**' click on '**AzGovViz.variables.yml**' and click '**Edit**'
+1. If needed, modify the '**parameters**' section:
+ - For more information, see [Runtime parameters](https://learn.microsoft.com/azure/devops/pipelines/process/runtime-parameters).
+ - (Optional) Update the '**ExcludedResourceTypesDiagnosticsCapableParameters**'
+ - (Optional) Update the '**SubscriptionQuotaIdWhitelistParameters**'
+1. Update the '**Required Variables**' section:
+ - Replace `` with the name of the Azure DevOps service connection that you created and configured earlier (ServiceConnection)
+ - Replace `` with the target Azure management group ID (ManagementGroupId)
+1. If needed, update the '**Default Variables**' section
+1. If needed, update the '**Optional Variables**' section
+
+## 7. Create the Azure pipeline
+
+1. Open your project in [Azure DevOps](http://dev.azure.com/).
+1. Click on '**Pipelines**'
+1. Click on '**New pipeline**'
+1. Select '**Azure Repos Git**'
+1. Select the Azure Governance Visualizer repository
+1. Click on '**Existing Azure Pipelines YAML file**'
+1. Under '**Path**' select '**/.azuredevops/pipelines/AzGovViz.pipeline.yml**'
+1. Click ' **Save**'
+
+## 8. Test the Azure pipeline with a manual run
+
+1. Click on '**Pipelines**'
+1. Select the new Azure Governance Visualizer pipeline
+1. Click '**Run pipeline**'
+
+> Before the pipeline kicks off it may require you to approve the run. (only first time run)
+
+## 9. Establish an Azure DevOps Wiki (WikiAsCode)
+
+Once the pipeline has executed successfully you can setup your Wiki
+
+1. Click on '**Overview**'
+1. Click on '**Wiki**'
+1. Click on '**Publish code as wiki**'
+1. Select the Azure Governance Visualizer repository
+1. Select the folder '**wiki**' and click '**OK**'
+1. Enter a name for the Wiki
+1. Click '**Publish**'
+
+## Next steps
+
+For report hosting, consider using the [Azure Governance Visualizer accelerator](https://github.com/Azure/Azure-Governance-Visualizer-Accelerator) which will give you an example on how to host the output on Azure Web Apps in conjunction with this Azure DevOps automation.
diff --git a/setup/azure-web-app.md b/setup/azure-web-app.md
new file mode 100644
index 00000000..2d34a280
--- /dev/null
+++ b/setup/azure-web-app.md
@@ -0,0 +1,34 @@
+# Optional Publishing the Azure Governance Visualizer HTML to a Azure Web App
+
+There are instances where you may want to publish the HTML output to a webapp so that anybody in the business can see up to date status of the Azure governance.
+
+You can either setup the azure Web App [manually](#manual-setup) or deploy [code based](#code-based-setup) using the Azure Governance Visualizer accelerator.
+
+## Manual setup
+
+### Prerequisites
+* Deploy a Web App on Azure. This can be the smallest SKU or a FREE SKU. It doesn't matter whether you choose Windows or Linux as the platform
+![alt text](../img/webapp_create.png "Azure Web App Create")
+* Step through the configuration. I typically use the Code for the publish and then select the Runtime stack that you standardize on
+![alt text](../img/webapp_configure.png "Azure Web App Configure")
+* No need to configure anything, unless your organization policies require you to do so
+NOTE: it is a good practice to tag your resource for operational and finance reasons
+* In the webapp _Configuration_ add the name of the HTML output file to the _Default Documents_
+![alt text](../img/webapp_defaultdocs.png "Azure Web App Default documents")
+* Make sure to configure Authentication!
+![alt text](../img/webapp_authentication.png "Azure Web App Authentication")
+
+### Azure DevOps
+
+* Assign the Azure DevOps Service Connection´s Service Principal with RBAC Role __Website Contributor__ on the Azure Web App
+* Edit the `.azuredevops/AzGovViz.variables.yml` file
+![alt text](../img/webapp_AzDO_yml.png "Azure DevOps YAML variables")
+
+### GitHub Actions
+
+* Assign the Service Principal used in GitHub with RBAC Role __Website Contributor__ on the Azure Web App
+* Edit the `.github/workflows/AzGovViz_OIDC.yml` or `.github/workflows/AzGovViz.yml` file
+![alt text](../img/webapp_GitHub_yml.png "GitHub YAML variables")
+
+## Code based setup
+Use the [Azure Governance Visualizer accelerator](https://github.com/Azure/Azure-Governance-Visualizer-Accelerator) to deploy the Azure Web App per code.
\ No newline at end of file
diff --git a/setup/console.md b/setup/console.md
new file mode 100644
index 00000000..ec7253e1
--- /dev/null
+++ b/setup/console.md
@@ -0,0 +1,164 @@
+
+# Configure and run Azure Governance Visualizer from the console
+
+When trying out Azure Governance Visualizer for the first time or simply as a one-time evaluation of an Azure tenant, the quickest way to get results is to run it directly from the console. These instructions will get you up and running from a terminal.
+
+## Prerequisites
+
+The following must be installed on the workstation that will be used to run the scripts:
+
+- [Git](https://git-scm.com/downloads)
+- [PowerShell 7](https://github.com/PowerShell/PowerShell#get-powershell) (minimum supported version 7.0.3)
+- [Azure PowerShell](https://learn.microsoft.com/powershell/azure/install-azure-powershell), specifically `Az.Accounts`.
+- [AzAPICall](https://github.com/JulianHayward/AzAPICall#get--set-azapicall-powershell-module)
+
+> There is a [dev container provided in this repo](#using-github-codespaces-as-your-console) if you'd wish to use GitHub Codespaces.
+
+## 1. Validate Microsoft Graph permissions for your user
+
+:arrow_forward: If your user is a tenant _member user_ and you plan on running the script as yourself, then no additional setup is necessary. This is the most common. You can :arrow_down_small: continue with [**2. Validate Azure permissions for your user**](#2-validate-azure-permissions-for-your-user).
+
+_- or -_
+
+:arrow_forward: However, if your user is tenant _guest user_ and you plan on running the script as yourself, continue to [Set up to execute as a tenant _guest user_](#set-up-to-execute-as-a-tenant-guest-user) to ensure your user is configured properly. You will likely need support from the Microsoft Entra ID administrator of the tenant you are a guest in.
+
+_- or -_
+
+:arrow_forward: If instead you are planning on executing the script as a pre-existing service principal instead of as your user, see [Set up to execute as a _service principal_](#set-up-to-execute-as-a-service-principal) to ensure it is configured properly.
+
+### Set up to execute as a tenant _guest user_
+
+Your user is a [guest user](https://learn.microsoft.com/entra/fundamentals/users-default-permissions#compare-member-and-guest-default-permissions) in the tenant or there are other [hardened restrictions](https://learn.microsoft.com/entra/identity/users/users-restrict-guest-permissions) on the tenant, then your user must first be assigned the Microsoft Entra ID role '**Directory readers**'. Work with the Microsoft Entra administrator for the tenant you are a guest in to have them assign the '**Directory readers**' [role to your guest account](https://learn.microsoft.com/entra/identity/role-based-access-control/manage-roles-portal).
+
+:arrow_down_small: Once that is configured, continue with [**2. Validate Azure permissions for your user**](#2-validate-azure-permissions-for-your-user).
+
+### Set up to execute as a _service principal_
+
+You are planning on executing the script as a service principal instead of as your user. A service principal, by default, has no read permissions on users, groups, and other service principals, therefore you'll need to work with a Microsoft Entra ID administrator to grant additional permissions to the service principal. The following Microsoft Graph API permissions, with admin consent, need to be granted:
+
+- '**Application / Application.Read.All**'
+- '**Group / Group.Read.All**'
+- '**User / User.Read.All**'
+- '**PrivilegedAccess / PrivilegedAccess.Read.AzureResources**'
+
+#### Assign Microsoft Graph permissions, if needed
+
+**:computer_mouse: Use the Microsoft Entra admin center to assign permissions to the service principal:**
+
+> To grant API permissions and admin consent for the directory, the user performing the following steps must have '**Privileged Role Administrator**' or '**Global Administrator**' role assigned in Microsoft Entra ID.
+
+1. Navigate to the [Microsoft Entra admin center](https://entra.microsoft.com/).
+1. Click on '**App registrations**'
+1. Search for the existing application (service principal)
+1. Under '**Manage**' click on '**API permissions**'
+1. Click on '**Add a permissions**'
+1. Click on '**Microsoft Graph**'
+1. Click on '**Application permissions**'
+1. Select the following set of permissions and click '**Add permissions**'
+ - **Application / Application.Read.All**
+ - **Group / Group.Read.All**
+ - **User / User.Read.All**
+ - **PrivilegedAccess / PrivilegedAccess.Read.AzureResources**
+1. Click on 'Add a permissions'
+1. Back in the main '**API permissions**' menu you will find permissions with status 'Not granted for...'. Click on '**Grant admin consent for _TenantName_**' and confirm by click on '**Yes**'
+ - Now you will find the permissions with status '**Granted for _TenantName_**'
+
+Permissions and admin consent granted in Microsoft Entra ID for the service principal (App Registration):
+
+![Permissions in Microsoft Entra ID](../img/aadpermissionsportal_4.jpg)
+
+## 2. Validate Azure permissions for your user
+
+The identity executing the script (your user or the service principal) needs to have the '**Reader**' Azure RBAC role assignment on the **target management group**.
+
+### Assign Azure permissions, if needed
+
+If that permission is not yet assigned to your user or the service principal, a user with '**Microsoft.Authorization/roleAssignments/write**' permissions on the target management group scope (such as the built-in Azure RBAC role '**User Access Administrator**' or '**Owner**') is required to make the required permission changes.
+
+**:computer_mouse: Use the Azure portal to validate and assign the role:**
+
+Follow the instructions at [Assign Azure roles using the Azure portal](https://learn.microsoft.com/azure/role-based-access-control/role-assignments-portal) to grant Azure RBAC '**Reader**' role to the management group.
+
+**:keyboard: Or use PowerShell to assign the role:**
+
+```powershell
+$objectId = ""
+$managementGroupId = ""
+
+New-AzRoleAssignment `
+-ObjectId $objectId `
+-RoleDefinitionName "Reader" `
+-Scope /providers/Microsoft.Management/managementGroups/$managementGroupId
+```
+
+## 3. Clone the Azure Governance Visualizer repository
+
+You'll need a copy of this repository on your workstation.
+
+```powershell
+git clone "https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting.git"
+Set-Location "Azure-MG-Sub-Governance-Reporting"
+```
+
+## 4. Authenticate to Azure
+
+**As your user:**
+
+```powershell
+Connect-AzAccount -TenantId -UseDeviceAuthentication
+```
+
+_- or -_
+
+**As the service principal:**
+
+Have the '**Application (client) ID**' of the app registration OR '**Application ID**' of the service principal (Enterprise application) and the secret of the app registration at hand.
+
+```powershell
+$pscredential = Get-Credential
+Connect-AzAccount -ServicePrincipal -TenantId -Credential $pscredential
+```
+
+User: Enter '**Application (client) ID**' of the App registration OR '**Application ID**' of the service principal (Enterprise application)
+
+Password for user \: Enter App registration's secret
+
+## 5. Run the Azure Governance Visualizer
+
+Familiarize yourself with the available [parameters](../README.md#parameters) for Azure Governance Visualizer. The following example will create the output in directory **c:\AzGovViz-Output** (directory must exist)
+
+```powershell
+.\pwsh\AzGovVizParallel.ps1 -ManagementGroupId -OutputPath "c:\AzGovViz-Output"
+```
+
+## 6. View the results
+
+Open the generated HTML in your default browser.
+
+```powershell
+Set-Location -Path "c:\AzGovViz-Output"
+Get-ChildItem
+Invoke-Item ".\AzGovViz*.html"
+```
+
+There is also a markdown version available as well in the output directory.
+
+## Using GitHub Codespaces as your console
+
+This repo ships with a GitHub [Codespace](https://docs.github.com/codespaces/getting-started/quickstart) dev container configuration that has all of the [Prerequisites](#prerequisites) installed.
+
+### Visual screenshot tour of that experience
+
+![Azure Governance Visualizer GitHub Codespaces](../img/codespaces0.png)
+
+![Azure Governance Visualizer GitHub Codespaces](../img/codespaces1.png)
+
+![Azure Governance Visualizer GitHub Codespaces](../img/codespaces2.png)
+
+![Azure Governance Visualizer GitHub Codespaces](../img/codespaces3.png)
+
+![Azure Governance Visualizer GitHub Codespaces](../img/codespaces4.png)
+
+## Next steps
+
+Consider a solution that automates the execution of this process to have regular snapshots of this data available for review. This repo has instructions available to automate using [Azure DevOps](azure-devops.md) or [GitHub](github.md). For report hosting, consider using the [Azure Governance Visualizer accelerator](https://github.com/Azure/Azure-Governance-Visualizer-Accelerator) which will give you an example on how to host the output on Azure Web Apps in conjunction with the automation.
diff --git a/setup/github.md b/setup/github.md
new file mode 100644
index 00000000..167e7469
--- /dev/null
+++ b/setup/github.md
@@ -0,0 +1,126 @@
+
+# Configure and run Azure Governance Visualizer from GitHub
+
+GitHub can be used to orchestrate regular execution of Azure Governance Visualizer against your target management group. This allows headless, automated execution along with the ability to set least privileges on the executing account. It uses GitHub actions as the workflow orchestrator. These instructions will get you up and running from GitHub.
+
+## Prerequisites
+
+- A GitHub organization in which you have enough permissions to create a repository.
+
+## 1. Create GitHub repository
+
+1. Go to to start the repository creation process.
+1. Use '**https:\//github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting.git**' as the clone URL.
+1. Select your existing GitHub organization.
+1. Select 'Private'
+1. Click on 'Begin import'
+1. Navigate to your newly created repository
+
+If you'd instead like to perform this from the GitHub CLI, see [gh repo create](https://cli.github.com/manual/gh_repo_create) for instructions.
+
+## 2. Create and configure a service principal
+
+For GitHub actions to authenticate and connect to Azure you need to create a service principal. This will allow the Azure Governance Visualizer scripts to connect to Azure resources and Microsoft Graph with a properly permissioned identity.
+
+There are a few options to create the service principal, both will result in least privilege access:
+
+- **Option 1** - [Use workload identity federation](#option-1---use-workload-identity-federation-recommended) _(This is the recommended option.)_
+- **Option 2** - [Create and manage a service principal](#option-2---create-and-manage-a-service-principal)
+
+### Option 1 - Use workload identity federation (recommended)
+
+This option uses Microsoft Entra workload identity federation to manage a service principal you create but without also the need for you to manage secrets or secret expiration. This process uses the [OIDC (OpenID Connect) feature](https://docs.github.com/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure) of GitHub workflows. This process uses the **[.github/workflows/AzGovViz_OIDC.yml](../.github/workflows/AzGovViz_OIDC.yml)** workflow file and is the recommended method.
+
+1. Navigate to the [Microsoft Entra admin center](https://entra.microsoft.com/)
+1. Click on '**App registrations**'
+1. Click on '**New registration**'
+1. Name your application (e.g. 'AzureGovernanceVisualizer_SC')
+1. Click '**Register**'
+1. Your App registration has been created, in the '**Overview**' copy the '**Application (client) ID**' as we will need it later to setup the connection
+1. Under '**Manage**' click on '**Certificates & Secrets**'
+1. Click on '**Federated credentials**'
+1. Click 'Add credential'
+1. Select Federation credential scenario 'GitHub Actions deploying Azure Resources'
+1. Fill the field 'Organization' with your GitHub Organization name
+1. Fill the field 'Repository' with your GitHub repository name
+1. For the entity type select 'Branch'
+1. Fill the field 'GitHub branch name' with your branch name (default is 'master' if you imported the Azure Governance Visualizer repository)
+1. Fill the field 'Name' with a name (e.g. AzureGovernanceVisualizer_GitHub_Actions)
+1. Click 'Add'
+
+#### Store the service principal configuration in GitHub
+
+1. In the GitHub repository, navigate to 'Settings'
+1. Click on 'Secrets'
+1. Click on 'Actions'
+1. Click 'New repository secret'
+1. Create the following three secrets:
+ - Name: **CLIENT_ID**
+ Value: `Application (client) ID (GUID)`
+ - Name: **TENANT_ID**
+ Value: `Tenant ID (GUID)`
+ - Name: **SUBSCRIPTION_ID**
+ Value: `Subscription ID (GUID)`
+
+### Option 2 - Create and manage a service principal
+
+This other option has you creating a service principal and requires you to manage secrets and secret expiration for that service principal. This process uses the **[.github/workflows/AzGovViz.yml](../.github/workflows/AzGovViz.yml)** workflow file.
+
+1. Navigate to the [Microsoft Entra admin center](https://entra.microsoft.com/)
+1. Click on '**App registrations**'
+1. Name your application (e.g. 'AzureGovernanceVisualizer_SC')
+1. Click '**Register**'
+1. Your App registration has been created, in the '**Overview**' copy the '**Application (client) ID**' as we will need it later to setup the secrets in GitHub
+1. Under '**Manage**' click on '**Certificates & Secrets**'
+1. Click on '**New client secret**'
+1. Provide a good description and choose the expiry time based on your need and click '**Add**'
+1. A new client secret has been created, copy the secret's value as we will need it later to setup the secrets in GitHub
+
+#### Store the newly created credentials in GitHub
+
+1. In the GitHub repository, navigate to 'Settings'
+1. Click on 'Secrets'
+1. Click on 'Actions'
+1. Click 'New repository secret'
+ - Name: **CREDS**
+ - Value:
+
+ ```json
+ {
+ "tenantId": "",
+ "subscriptionId": "",
+ "clientId": "",
+ "clientSecret": ""
+ }
+ ```
+
+## 3. Set GitHub workflow permissions
+
+1. In the GitHub repository, navigate to 'Settings'
+1. Click on 'Actions'
+1. Click on 'General'
+1. Under 'Workflow permissions' select '**Read and write permissions**'
+1. Click 'Save'
+
+## 4. Configure the workflow YAML file
+
+1. In the folder `./github/workflows` edit the appropriate YAML file based on your choice in Step 2
+ - **[AzGovViz_OIDC.yml](../.github/workflows/AzGovViz_OIDC.yml)** for Option 1 (workload identity federation)
+ - **[AzGovViz.yml](../.github/workflows/AzGovViz.yml)** for Option 2 (Normal service principal)
+1. In the `env` section enter your target Azure management group ID
+1. If you want to continuously run Azure Governance Visualizer then enable the `schedule` in the `on` section
+
+## 5. Run Azure Governance Visualizer in GitHub actions
+
+1. In the GitHub repository, navigate to 'Actions'
+1. Click 'Enable GitHub Actions on this repository'
+1. Select the configured Azure Governance Visualizer workflow file
+1. Click 'Run workflow'
+
+## 6. Publish the Azure Governance Visualizer HTML to a Azure Web App _(Optional)_
+
+There are instances where you may want to publish the HTML output to a webapp so that anybody in the business can see up to date status of the Azure governance. The instructions for this can be found in the [Azure Governance Visualizer accelerator](https://github.com/Azure/Azure-Governance-Visualizer-Accelerator?tab=readme-ov-file#5-create-a-microsoft-entra-application-for-user-authentication-to-the-azure-web-app-that-will-host-azgovviz) repo.
+
+## Next steps
+
+For report hosting, consider using the [Azure Governance Visualizer accelerator](https://github.com/Azure/Azure-Governance-Visualizer-Accelerator) which will give you an example on how to host the output on Azure Web Apps in conjunction with this GitHub automation.
diff --git a/version.json b/version.json
index 441a8e59..4cbafd36 100644
--- a/version.json
+++ b/version.json
@@ -1,3 +1,3 @@
{
- "ProductVersion": "6.3.7"
+ "ProductVersion": "6.4.0"
}
\ No newline at end of file