Skip to content

Commit

Permalink
v6_major_20220531_1
Browse files Browse the repository at this point in the history
  • Loading branch information
JulianHayward committed May 31, 2022
1 parent 835f62b commit 4bce481
Show file tree
Hide file tree
Showing 12 changed files with 586 additions and 85 deletions.
12 changes: 6 additions & 6 deletions .azuredevops/pipelines/AzGovViz.variables.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# AzGovViz v6_major_20220521_1
# AzGovViz v6_major_20220531_1
# First things first:
# 1. Replace <YourServiceConnection> with the name of your service connection
# 2. Replace <YourManagementGroupId> with the your ManagementGroupId
Expand Down Expand Up @@ -76,11 +76,6 @@ variables:
# Integer | default = 14 | example: value: 21
value:

# Define for which time period Azure Consumption data should be gathered
- name: AzureConsumptionPeriod
# Integer | default = 1 | example: value: 7
value:

# Define the direction the Hierarchy should be built in Azure DevOps WikiAsCode (Markdown) TD = TopDown (Horizontal), LR = LeftRight (Vertical)
- name: AzureDevOpsWikiHierarchyDirection
# String | default = 'TD' | example: value: 'LR'
Expand All @@ -96,6 +91,11 @@ variables:
# Switch | example: value: true
value:

# If DoAzureConsumption == true then you may define for which time period (days) Azure Consumption data should be gathered
- name: AzureConsumptionPeriod
# Integer | default = 1 | example: value: 7
value:

# Do not include Role assignments on ResourceGroups and Resources
- name: DoNotIncludeResourceGroupsAndResourcesOnRBAC
# Switch | example: value: true
Expand Down
19 changes: 5 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,11 @@ Listed as [security monitoring tool](https://docs.microsoft.com/en-us/azure/arch

## Release history

__Changes__ (2022-May-21 / Major)

> Note: Azure DevOps and GitHub users must update the YAML file(s) and PowerShell files (`AzGovVizParallel.ps1` and `prerequisites.ps1`)
* Integration of [PSRule for Azure](#integrate-psrule-for-azure). This feature is optional, use new parameter `-DoPSRule`
* Provides a [Azure Well-Architected Framework](https://docs.microsoft.com/en-gb/azure/architecture/framework/) aligned suite of rules for validating Azure resources
* Provides meaningful information to allow remediation
* New parameter `-PSRuleVersion` - Define the PSRule..Rules.Azure PowerShell module version, if undefined then 'latest' will be used
* Optional feature: publish HTML to Azure Web App (check the __[Setup Guide](setup.md)__) in Azure DevOps or GitHub Actions - thanks Wayne Meyer
* New feature / report on [enabled Subscription Features](https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/preview-features) TenantSummary, ScopeInsights and CSV export
* Decomissioned Azure DevOps `.pipelines` - use the new YAML files `.azuredevops/pipelines/*`
* Fix [#issue92](https://github.com/JulianHayward/Azure-MG-Sub-Governance-Reporting/issues/92) -> pipeline .azuredevops/pipelines/AzGovViz.pipeline.yml
* Update Azure DevOps pipelines / use AzurePowershell@5
* Update prerequisites.ps1
__Changes__ (2022-May-31 / Major)

* New feature - Report on 'Classic Administrators' for Subscriptions -> TenantSummary, ScopeInsights and CSV export
* Fix consumption reporting (issue #101 - handle error: 'Management group `<ManagementGroupId>` does not have any valid subscriptions')
* PSRule for Azure / Azure DevOps dependencies (Az.Resources) workaround -> use PSRule for Azure version 1.14.3 (else latest)

Passed tests: Powershell Core 7.2.3 on Windows
Passed tests: Powershell Core 7.2.3 Azure DevOps hosted agent ubuntu-20.04
Expand Down
6 changes: 6 additions & 0 deletions history.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

### AzGovViz version 6

__Changes__ (2022-May-31 / Major)

* New feature - Report on 'Classic Administrators' for Subscriptions -> TenantSummary, ScopeInsights and CSV export
* Fix consumption reporting (issue #101 - handle error: 'Management group `<ManagementGroupId>` does not have any valid subscriptions')
* PSRule for Azure / Azure DevOps dependencies (Az.Resources) workaround -> use PSRule for Azure version 1.14.3 (else latest)

__Changes__ (2022-May-21 / Major)

> Note: Azure DevOps and GitHub users must update the YAML file(s) and PowerShell files (`AzGovVizParallel.ps1` and `prerequisites.ps1`)
Expand Down
316 changes: 284 additions & 32 deletions pwsh/AzGovVizParallel.ps1

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions pwsh/dev/devAzGovVizParallel.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ Param
$AzAPICallVersion = '1.1.12',

[string]
$ProductVersion = 'v6_major_20220521_1',
$ProductVersion = 'v6_major_20220531_1',

[string]
$GithubRepository = 'aka.ms/AzGovViz',
Expand Down Expand Up @@ -561,6 +561,13 @@ $null = $modules.Add([PSCustomObject]@{
})

if ($DoPSRule) {

#temporary workaround / PSRule/Azure DevOps Az.Resources module requirements
if ($env:SYSTEM_TEAMPROJECTID -and $env:BUILD_REPOSITORY_ID) {
$PSRuleVersion = '1.14.3'
Write-Host "Running in Azure DevOps; enforce PSRule version '$PSRuleVersion' (Az.Resources dependency on latest PSRule)"
}

$null = $modules.Add([PSCustomObject]@{
ModuleName = 'PSRule.Rules.Azure'
ModuleVersion = $PSRuleVersion
Expand Down Expand Up @@ -717,7 +724,6 @@ if ($azAPICallConf['htParameters'].HierarchyMapOnly -eq $false) {
$htPolicyAssignmentManagedIdentity = @{}
$htManagedIdentityDisplayName = @{}
$htAppDetails = [System.Collections.Hashtable]::Synchronized((New-Object System.Collections.Hashtable)) #@{}

if (-not $NoAADGroupsResolveMembers) {

$htAADGroupsDetails = [System.Collections.Hashtable]::Synchronized((New-Object System.Collections.Hashtable)) #@{}
Expand All @@ -726,13 +732,12 @@ if ($azAPICallConf['htParameters'].HierarchyMapOnly -eq $false) {
$arrayGroupRequestResourceNotFound = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
$arrayProgressedAADGroups = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
}

if ($DoAzureConsumption) {
$allConsumptionData = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
}

$arrayPsRule = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
$arrayPSRuleTracking = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
$htClassicAdministrators = [System.Collections.Hashtable]::Synchronized((New-Object System.Collections.Hashtable)) #@{}
}

getEntities
Expand Down
51 changes: 50 additions & 1 deletion pwsh/dev/functions/dataCollection/dataCollectionFunctions.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,18 @@ function dataCollectionResources {
if ($azAPICallConf['htParameters'].DoPSRule -eq $true) {
if ($resourcesSubscriptionResult.Count -gt 0) {
$startPSRule = Get-Date
$psruleResults = $resourcesSubscriptionResult | Invoke-PSRule -Module psrule.rules.azure -As Detail -Culture en-us -WarningAction Ignore -ErrorAction SilentlyContinue
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')
#>
$psruleResults = $resourcesSubscriptionResult | 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)

Expand Down Expand Up @@ -2904,4 +2915,42 @@ function dataCollectionRoleAssignmentsSub {
}
$funcDataCollectionRoleAssignmentsSub = $function:dataCollectionRoleAssignmentsSub.ToString()

function dataCollectionClassicAdministratorsSub {
[CmdletBinding()]Param(
[string]$scopeId,
[string]$scopeDisplayName,
[string]$subscriptionMgPath
)

$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')"
AzAPICallConfiguration = $azAPICallConf
}

$AzApiCallResult = AzAPICall @azAPICallPayload
$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
27 changes: 13 additions & 14 deletions pwsh/dev/functions/getConsumption.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function getConsumption {
},
{
"type": "Dimension",
"name": "ConsumedService"
"name": "ResourceType"
},
{
"type": "Dimension",
Expand Down Expand Up @@ -108,7 +108,7 @@ function getConsumption {
}
#>

if ($mgConsumptionData -eq 'Unauthorized' -or $mgConsumptionData -eq 'OfferNotSupported') {
if ($mgConsumptionData -eq 'Unauthorized' -or $mgConsumptionData -eq 'OfferNotSupported' -or $mgConsumptionData -eq 'NoValidSubscriptions') {
if (-not $script:htConsumptionExceptionLog.Mg.($ManagementGroupId)) {
$script:htConsumptionExceptionLog.Mg.($ManagementGroupId) = @{}
}
Expand Down Expand Up @@ -139,7 +139,7 @@ function getConsumption {
},
{
"type": "Dimension",
"name": "ConsumedService"
"name": "ResourceType"
},
{
"type": "Dimension",
Expand Down Expand Up @@ -269,7 +269,7 @@ function getConsumption {
},
{
"type": "Dimension",
"name": "ConsumedService"
"name": "ResourceType"
},
{
"type": "Dimension",
Expand All @@ -295,17 +295,16 @@ function getConsumption {
#test
#$allConsumptionData = "OfferNotSupported"

if ($allConsumptionDataAPIResult -eq 'AccountCostDisabled' -or $allConsumptionDataAPIResult -eq 'NoValidSubscriptions') {
$generalShowStopperResult = $true
if ($allConsumptionDataAPIResult -eq 'AccountCostDisabled' <#-or $allConsumptionDataAPIResult -eq 'NoValidSubscriptions'#>) {
if ($allConsumptionDataAPIResult -eq 'AccountCostDisabled') {
$detailShowStopperResult = $allConsumptionDataAPIResult
}
if ($allConsumptionDataAPIResult -eq 'NoValidSubscriptions') {
<#if ($allConsumptionDataAPIResult -eq 'NoValidSubscriptions') {
$detailShowStopperResult = $allConsumptionDataAPIResult
}
}#>
}
else {
if ($allConsumptionDataAPIResult -eq 'Unauthorized' -or $allConsumptionDataAPIResult -eq 'OfferNotSupported') {
if ($allConsumptionDataAPIResult -eq 'Unauthorized' -or $allConsumptionDataAPIResult -eq 'OfferNotSupported' -or $allConsumptionDataAPIResult -eq 'NoValidSubscriptions') {
$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."
Expand All @@ -332,7 +331,7 @@ function getConsumption {
},
{
"type": "Dimension",
"name": "ConsumedService"
"name": "ResourceType"
},
{
"type": "Dimension",
Expand Down Expand Up @@ -465,7 +464,7 @@ function getConsumption {
$script:htAzureConsumptionSubscriptions.($subscriptionId.Name).ConsumptionData = $subscriptionId.group
$script:htAzureConsumptionSubscriptions.($subscriptionId.Name).TotalCost = $subTotalCost
$script:htAzureConsumptionSubscriptions.($subscriptionId.Name).Currency = $currency.Name
$resourceTypes = $subscriptionId.Group.ConsumedService | Sort-Object -Unique
$resourceTypes = $subscriptionId.Group.ResourceType | Sort-Object -Unique

foreach ($parentMg in $htSubscriptionsMgPath.($subscriptionId.Name).ParentNameChain) {

Expand Down Expand Up @@ -532,9 +531,9 @@ function getConsumption {
}

$totalCost = 0
$script:tenantSummaryConsumptionDataGrouped = $currency.group | Group-Object -property ConsumedService, ChargeType, MeterCategory
$script:tenantSummaryConsumptionDataGrouped = $currency.group | Group-Object -property ResourceType, ChargeType, MeterCategory
$subsCount = ($tenantSummaryConsumptionDataGrouped.group.subscriptionId | Sort-Object -Unique | Measure-Object).Count
$consumedServiceCount = ($tenantSummaryConsumptionDataGrouped.group.consumedService | 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) {

Expand All @@ -548,7 +547,7 @@ function getConsumption {
}

$null = $script:arrayConsumptionData.Add([PSCustomObject]@{
ConsumedService = ($consumptionline.name).split(', ')[0]
ResourceType = ($consumptionline.name).split(', ')[0]
ConsumedServiceChargeType = ($consumptionline.name).split(', ')[1]
ConsumedServiceCategory = ($consumptionline.name).split(', ')[2]
ConsumedServiceInstanceCount = $consumptionline.Count
Expand Down
6 changes: 5 additions & 1 deletion pwsh/dev/functions/processDataCollection.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,9 @@ function processDataCollection {
$arrayDefenderPlansSubscriptionNotRegistered = $using:arrayDefenderPlansSubscriptionNotRegistered
$arrayUserAssignedIdentities4Resources = $using:arrayUserAssignedIdentities4Resources
$htSubscriptionsRoleAssignmentLimit = $using:htSubscriptionsRoleAssignmentLimit
$PSRuleVersion = $using:PSRuleVersion
$arrayPsRule = $using:arrayPsRule
$arrayPSRuleTracking = $using:arrayPSRuleTracking
$htClassicAdministrators = $using:htClassicAdministrators
#other
$function:addRowToTable = $using:funcAddRowToTable
$function:namingValidation = $using:funcNamingValidation
Expand All @@ -349,6 +349,7 @@ function processDataCollection {
$function:dataCollectionPolicyAssignmentsSub = $using:funcDataCollectionPolicyAssignmentsSub
$function:dataCollectionRoleDefinitions = $using:funcDataCollectionRoleDefinitions
$function:dataCollectionRoleAssignmentsSub = $using:funcDataCollectionRoleAssignmentsSub
$function:dataCollectionClassicAdministratorsSub = $using:funcDataCollectionClassicAdministratorsSub
#endregion UsingVARs

$addRowToTableDone = $false
Expand Down Expand Up @@ -496,6 +497,9 @@ function processDataCollection {
if ($functionReturn.'addRowToTableDone') {
$addRowToTableDone = $true
}

#SubscriptionClassicAdministrators
dataCollectionClassicAdministratorsSub @baseParameters -SubscriptionMgPath $childMgMgPath
}

if ($addRowToTableDone -ne $true) {
Expand Down
Loading

0 comments on commit 4bce481

Please sign in to comment.