Azure Governance Visualizer is a PowerShell based script that iterates your Azure Tenant´s Management Group hierarchy down to Subscription level. It captures most relevant Azure governance capabilities such as Azure Policy, RBAC and Blueprints and a lot more. From the collected data Azure Governance Visualizer provides visibility on your HierarchyMap, creates a TenantSummary, creates DefinitionInsights and builds granular ScopeInsights on Management Groups and Subscriptions. This accelerator speeds up the adoption of the script into your environment.
- Azure Governance Visualizer (AzGovViz) Accelerator
- Description
- Table of Contents
- Prerequisites
- 1. Create a Service Principal (Azure AD app registration) to run AzGovViz
- 2. Create the GitHub repository
- 3. Configure federated credentials for the Service Principal
- 4. Grant permissions in Azure for the AzGovViz service principal
- 5. Create an Azure AD application for AAD authentication for the Azure Web App
- 6. Create a Resource Group and assign the right RBAC Roles
- 7. Create the GitHub secrets, variables and permissions
- How to deploy
- Configuration
- Sources to documentation
NOTE 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
- Navigate to 'Azure Active Directory'
- Click on 'App registrations'
- Click on 'New registration'
- Name your application (e.g. AzureGovernanceVisualizer_SP)
- 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 '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'
-
NOTE 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
-
Install AzAPICall and connect to Azure
$module = Get-Module -Name "AzAPICall" -ListAvailable if ($module) { Update-Module -Name "AzAPICall" -Force } else { Install-Module -Name AzAPICall } Connect-AzAccount
-
Initialize AzAPICall
$parameters4AzAPICallModule = @{ #SubscriptionId4AzContext = $null #specify Subscription Id #DebugAzAPICall = $true #WriteMethod = 'Output' #Debug, Error, Host, Information, Output, Progress, Verbose, Warning (default: host) #DebugWriteMethod = 'Warning' #Debug, Error, Host, Information, Output, Progress, Verbose, Warning (default: host) #SkipAzContextSubscriptionValidation = $true #Use if the account doesn´t have any permissions on Management Groups, Subscriptions, Resource Groups or Resources } $azAPICallConf = initAzAPICall @parameters4AzAPICallModule
-
Define variables
$MicrosoftGraphAppId = "00000003-0000-0000-c000-000000000000" $AzGovVizAppName = "<App registration name that will be used to run AzGovViz>"
-
Get Microsoft Graph permissions role Ids and create app registration
$apiEndPoint = $azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph
$apiEndPointVersion = '/v1.0'
$api = '/servicePrincipals'
$optionalQueryParameters = "?`$filter=(displayName eq 'Microsoft Graph')&$count=true&"
$uri = $apiEndPoint + $apiEndPointVersion + $api + $optionalQueryParameters
$azAPICallPayload = @{
uri= $uri
method= 'GET'
currentTask= "'$($azAPICallConf['azAPIEndpoints'].($apiEndPoint.split('/')[2])) API: Get - Groups'"
consistencyLevel= 'eventual'
noPaging= $true
AzAPICallConfiguration = $azAPICallConf
}
$graphApp = AzAPICall @azAPICallPayload
$appRole = $graphApp.appRoles | Where-Object { $_.value -eq 'Application.Read.All' } | Select-Object -ExpandProperty id
$userRole = $graphApp.appRoles | Where-Object { $_.value -eq 'User.Read.All' } | Select-Object -ExpandProperty id
$groupRole = $graphApp.appRoles | Where-Object { $_.value -eq 'Group.Read.All' } | Select-Object -ExpandProperty id
$pimRole = $graphApp.appRoles | Where-Object { $_.value -eq 'PrivilegedAccess.Read.AzureResources' } | Select-Object -ExpandProperty id
$body = @"
{
"DisplayName":"$AzGovVizAppName",
"requiredResourceAccess" : [
{
"resourceAppId" : "$MicrosoftGraphAppId",
"resourceAccess": [
{
"id": "$appRole",
"type": "Role"
},
{
"id": "$userRole",
"type": "Role"
},
{
"id": "$groupRole",
"type": "Role"
},
{
"id": "$pimRole",
"type": "Role"
}
]
}
]
}
"@
$AzGovVizAppObjectId = (AzAPICall -method POST -body $body -uri "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications" -AzAPICallConfiguration $azAPICallConf -listenOn 'Content' -consistencyLevel 'eventual').id
do {
Write-Host "Waiting for the AzGovViz service principal to get created..."
Start-Sleep -seconds 20
$AzGovVizAppId = (AzAPICall -method GET -uri "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications/$AzGovVizAppObjectId" -AzAPICallConfiguration $azAPICallConf -listenOn 'Content' -consistencyLevel 'eventual' -skipOnErrorCode 404).appId
} until ($null -ne $AzGovVizAppId)
Write-host "AzGovViz service principal created successfully."
-
Navigate to the accelerator GitHub repository
NOTE The new repository's visibility needs to be set as Private.
-
Login to your GitHub account
gh auth login
-
Create a private repository from the Accelerator template
### Define variables $directoryToCloneAccelerator = "<Local directory to clone the Accelerator's repository>" $GitHubOrg = "<GitHub organization to use>" $GitHubRepository = "Azure-Governance-Visualizer" ### Create a new repository from template gh repo create $GitHubRepository --template Azure/Azure-Governance-Visualizer-Accelerator --private New-Item -ItemType Directory -Path $directoryToCloneAccelerator -Force cd $directoryToCloneAccelerator gh repo clone "$GitHubOrg/$GitHubRepository" Set-Location $GitHubRepository
Navigate to 'Azure Active Directory'
- Click on 'App registrations'
- Search for the Application that we created earlier and click on it
- 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
- Fill the field 'Name' with a name (e.g. AzureGovernanceVisualizer_GitHub_Actions)
- Click 'Add'
$gitHubRef= ":ref:refs/heads/main"
$subject = "repo:$gitHubOrg/$GitHubRepository$gitHubRef"
$body = @"
{
"audiences": [
"api://AzureADTokenExchange"
],
"subject":"$subject",
"issuer":"https://token.actions.githubusercontent.com",
"name":"AzGovVizCreds"
}
"@
AzAPICall -method POST -body $body -uri "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications/$AzGovVizAppObjectId/federatedIdentityCredentials" -AzAPICallConfiguration $azAPICallConf -listenOn 'Content' -consistencyLevel 'eventual'NOTE 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 identity that shall run Azure Governance Visualizer
$role = "Reader"
$managementGroupId = "<managementGroupId>"
New-AzRoleAssignment `
-ApplicationId $AzGovVizAppId `
-RoleDefinitionName $role `
-Scope /providers/Microsoft.Management/managementGroups/$managementGroupId-
Create an app registration in Azure AD for your Azure App Web app
-
In the Redirect URIs section, select Web for platform and type the URI in the following format: "https://<webapp_name>.azurewebsites.net/.auth/login/aad/callback"
-
Click on Authentication and under Implicit grant and hybrid flows, enable ID tokens to allow OpenID Connect user sign-ins from App Service. Select Save.
-
From the left navigation, select Expose an API > Add > Save.
-
Click on Add a scope and provide the values as the screenshot.
# 2-60 Alphanumeric, hyphens and Unicode characters.Can't start or end with hyphen. A web site must have a globally unique name
$webAppName = "<Azure Web App name to publish AzGovViz>"
$WebApplicationAppName = "<App registration name that will be used to add Azure AD authentication to the web app>"
$body = @"
{
"DisplayName":"$WebApplicationAppName",
"web":
{
"redirectUris": [
"https://$webAppName.azurewebsites.net/.auth/login/aad/callback"
],
"implicitGrantSettings":
{
"enableIdTokenIssuance": true
}
}
}
"@
$webAppSP = AzAPICall -method POST -body $body -uri "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications" -AzAPICallConfiguration $azAPICallConf -listenOn 'Content' -consistencyLevel 'eventual'
$webAppSPAppId = $webAppSP.appId
$webAppSPObjectId = $webAppSP.Id
do {
Write-Host "Waiting for the Azure WebApp app registration to get created..."
Start-Sleep -seconds 30
$webApp = AzAPICall -uri "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications/$webAppSPObjectId" -AzAPICallConfiguration $azAPICallConf -listenOn 'Content' -consistencyLevel 'eventual'
} until ( $null -ne $webApp)
Write-host "Azure WebApp app registration created successfully."
#### Add an API scope for the Web App
$body = @"
{
"identifierUris" : [
"api://$webAppSPAppId"
],
"api":
{
"oauth2PermissionScopes": [
{
"value": "user_impersonation",
"adminConsentDescription": "AzGovViz Web App Azure AD authentication",
"adminConsentDisplayName": "AzGovViz Web App Azure AD authentication",
"type": "User",
"id": "$webAppSPAppId"
}
]
}
}
"@
AzAPICall -method PATCH -body $body -uri "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications/$webAppSPObjectId" -AzAPICallConfiguration $azAPICallConf -listenOn 'Content' -consistencyLevel 'eventual'
#### Generate client secret
$body = @"
{
"passwordCredential":{
"displayName": "AzGovVizWebAppSecret"
}
}
"@
$webAppSPAppSecret = (AzAPICall -method POST -body $body -uri "$($azAPICallConf['azAPIEndpointUrls'].MicrosoftGraph)/v1.0/applications/$webAppSPObjectId/addPassword" -AzAPICallConfiguration $azAPICallConf -listenOn 'Content' -consistencyLevel 'eventual').secretTextNOTE 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 new Resource Group in Azure
- Assign the following roles to the AzGovViz Service Principal the on the newly created Resource Group.
NOTE 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')
$subscriptionId = "<Subscription Id>"
$resourceGroupName = "Name of the Resource Group where the Azure Web App will be created>"
$location = "<Azure Region for the Azure Web App>"
Select-AzSubscription -SubscriptionId $subscriptionId
New-AzResourceGroup -Name $resourceGroupName -Location $location
New-AzRoleAssignment -ApplicationId $AzGovVizAppId -RoleDefinitionName "Web Plan Contributor" -ResourceGroupName $resourceGroupName
New-AzRoleAssignment -ApplicationId $AzGovVizAppId -RoleDefinitionName "WebSite Contributor" -ResourceGroupName $resourceGroupName
NOTE Make sure that the resource provider Microsoft.Web is registered on the subscription where the web app hosting AzGovViz will be hosted.
- Create the following GitHub secrets on the repository
| Secret | Value |
|---|---|
| CLIENT_ID | Application Id of the identity that shall run Azure Governance Visualizer |
| AAD_CLIENT_ID | Application Id of the identity that will be used to configure Azure AD authentication to the Azure Web App |
| AAD_CLIENT_SECRET | Secret of the identity that will be used to configure Azure AD authentication to the Azure Web App |
| SUBSCRIPTION_ID | Subscription Id |
| TENANT_ID | Tenant Id |
| MANAGEMENT_GROUP_ID | Management group Id |
- Create the following GitHub variables on the repository
| Variable | Value |
|---|---|
| RESOURCE_GROUP_NAME | Name of the pre-created resource group to host the Azure Web App |
| WEB_APP_NAME | Globally unique name of the Azure Web App |
- Enable GitHub actions to create and approve pull requests on the repository.
-
Create the needed secrets, variables and permissions
### Define variables $subscriptionId = "<Subscription Id>" $tenantId = "<Tenant Id>" $managementGroupId = $managementGroupId $resourceGroupName = $resourceGroupName $clientId = $AzGovVizAppId $aadClientId = $webAppSPAppId $aadClientSecret = $webAppSPAppSecret ### Create GitHub repository secrets and variables gh secret set 'CLIENT_ID' -b $clientId gh secret set 'AAD_CLIENT_ID' -b $aadClientId gh secret set 'AAD_CLIENT_SECRET' -b $aadClientSecret gh secret set 'SUBSCRIPTION_ID' -b $subscriptionId gh secret set 'TENANT_ID' -b $tenantId gh secret set 'MANAGEMENT_GROUP_ID' -b $managementGroupId gh variable set 'RESOURCE_GROUP_NAME' -b $resourceGroupName gh variable set 'WEB_APP_NAME' -b $webAppName ### Configure GitHub actions permissions gh api -X PUT /repos/$GitHubOrg/$GitHubRepository/actions/permissions/workflow -F can_approve_pull_request_reviews=true
To deploy the accelerator after having the pre-requisites ready, you need to perform the following steps:
-
Navigate to Actions in your newly created repository
-
Run the DeployAzGovVizAccelerator workflow to initialize the accelerator, deploy the Azure Web App and configure Azure AD authentication for it
-
This workflow will trigger another workflow to sync the latest AzGovViz code to your repository
-
You will have to add the AzGovViz parameters you need into the DeployAzGovViz workflow and enable the schedule option if you want to continuously run Azure Governance Visualizer.
-
As an example, I will add the NoPIMEligibility parameter since I don't have PIM
-
Then, run the DeployAzGovViz workflow to deploy AzGovViz and publish it to the Azure Web App
-
You can configure some aspects of the Azure Web application where AzGovViz is published by editing the webApp.parameters.json file in the bicep folder.
-
To keep the Azure Governance Visualizer's code up-to-date, the workflow SyncAzGovViz runs on a schedule to check for new versions. The default setting is that this is enabled to push updates automatically to your repository. If you need to control those new version updates, you will have to set AutoUpdateAzGovViz to false so you would get a Pull Request every time there is a new version to review.
- To keep the Azure Governance Visualizer Accelerator code up-to-date, the workflow SyncAccelerator runs on a schedule to check for new versions. Everytime there is a new update to the accelerator's code, you would get a Pull Request submitted to your repository and the new release will be merged to a releases folder where you can move to newer versions of this accelerator at your own pace.
For more information on Azure Governance Visualizer, please visit the official docs.




















