Create an AzKeyVault RBAC Secret Report Using PowerShell

Problem Statement:

Implementing the new RBAC Key Vault feature brings with it a more complex security model that makes it more difficult to track who has access to what. In this post I create a PowerShell script to provide basic reporting of who has access to your vault secrets and how they have the access. While creating the report as an Excel workbook is the straight forward approach, for the purpose of experimenting and gaining some additional skills I’m going to produce this report in MS Word format.

Required PowerShell Modules:

PScribo is an open source module that adds cmdlets to PowerShell to create a document structure that can be exported in multiple formats including MS Word.

Solution:

My goal is to create a document with the structure visualized below.

Script Outline:

  1. Get all the secrets from each vault I have access to in the tenant.
  2. For each secret derive the secret scope string
  3. For each scope string get the associated role assignments
  4. For each role assigned to a group, get the group members
  5. Using Pscribo comandlets create a document structure for the collected data
  6. Generate the Word document

function Invoke-Report.KVSecrets {
    <#
    .SYNOPSIS
        PowerShell script to document the RBAC settings for Azure Key Vaults
    .DESCRIPTION
        Documents the RBAC settings for Key Vault Secrets in Word format using PScribo.
    .NOTES
        Version:        0.0.1
        Author:         Chris Sharp
        Credits:        Iain Brighton (@iainbrighton) - PScribo module
    .LINK
       
    #>

[CmdletBinding()]
param (
    [System.String[]] $Format = @('Word'),
    [System.String] $Path = '.\',
    [bool]$IncludeSecretValue = $false,
    [PSCredential] $Credential
 
)

Import-Module PScribo -Force;
# Data Collection
$vaults = Get-AzKeyVault
$secretArray = @()
$secret_role_assignments = @()
$group = @()
$localUser = $env:UserName
$reportDate = Get-Date 
ForEach($vault in $vaults) {
    $vaultName = $vault.VaultName
    $vaultScope = $vault.ResourceID
    $secrets = Get-AzKeyVaultSecret -VaultName $vaultName
    $secrets | Add-Member -TypeName NoteProperty -NotePropertyName 'Scope' -NotePropertyValue ''
    $secrets | Add-Member -TypeName NoteProperty -NotePropertyName 'SecretValue' -NotePropertyValue ''
    $secrets | Add-Member -TypeName NoteProperty -NotePropertyName 'Vault' -NotePropertyValue $vaultName
    $secrets | ForEach-Object{$scope = "$vaultScope/secrets/$($_.Name)";$_.Scope = $scope}
    if($IncludeSecretValue) {
        
        $secrets | ForEach-Object{
            $sValue = (Get-AzKeyVaultSecret -VaultName $vault.VaultName -Name $_.Name).SecretValue
            $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sValue)
            $UnsecureSecret = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
            $_.SecretValue = $UnsecureSecret
        }
    
    } else {

        $secrets | ForEach-Object{$_.SecretValue = $_.SecretValue = '---'}        
    }

    $secretArray += $secrets

}
Foreach($secret in $secretArray) {
    $assignments = Get-AzRoleAssignment -Scope $secret.Scope
    $assignments | Add-Member -TypeName NoteProperty -NotePropertyName 'Secret' -NotePropertyValue $($secret.Name)
    $assignments | Add-Member -TypeName NoteProperty -NotePropertyName 'Vault' -NotePropertyValue $($secret.Vault)
    $secret_role_assignments += $assignments

}


Foreach($role_member in $secret_role_assignments) {
    if($($role_member.ObjectType) -eq 'Group') {

        $group += (Get-AzADGroup -DisplayName $($role_member.DisplayName))

    }
}
$group = $group | Sort-Object -Unique -Property 'ID'

Foreach($g in $group) {    

    $grpMember = Get-AzADGroupMember -GroupObjectId $($g.ID)
    $grpMember | Add-Member -TypeName NoteProperty -NotePropertyName 'Group' -NotePropertyValue $($g.DisplayName)
    $members += $grpMember
}

# End Data Collection


# Generate Report
<# The document name is used in the file output #>
$document = Document 'Key-Vault-Secret-Report' {  
    DocumentOption -PageSize Letter -MarginTopAndBottom 30 -MarginLeftAndRight 36

## Create document header and footer
    Style -Name 'Header' -Size 10 -Align Center
    Header -Default -IncludeOnFirstPage {
        Paragraph -Style 'Header' "Secrets Report - Generated by $localUser" 
    }

    Footer -Default {
        Paragraph -Style 'Header' 'Page <!# PageNumber #!> of <!# TotalPages #!>'
    }
## End Header/footer region

## Cover Page
Style -Name 'Cover1' -Size 24 -Align Center
Style -Name 'Cover2' -Size 12 -Align Center
BlankLine
LineBreak
Paragraph -Style 'Cover1' 'Azure Key Vault Roll Based Access Control Report' -Bold
LineBreak
BlankLine -Count 6
Paragraph -Style 'Cover2' "Report Generated on $reportDate"
PageBreak
## End Cover Page

    TOC -Name 'Table of Contents'
    PageBreak

    Paragraph -Style Heading1 "Azure Key Vault Report"
    Paragraph "This report provides a listing of all Secrets that $localUser has access to. It includes data from the following Azure Key Vaults:"
     Foreach($vault in $vaults) {
    
        Paragraph "-$($vault.VaultName)" -Tabs 1 

    }

    if($IncludeSecretValue){

        BlankLine -Count 2
        Paragraph "This report contains Secret Values in CLEAR TEXT. Protect accordingly." -Bold -Italic -Color red -Tabs 2 

    }

    BlankLine -Count 1
    LineBreak

    Section -Style Heading1 'Secrets Summary' {
            Paragraph "$($secretArray.Count) Secrets found:" -Font 'Arial' -Size 10

            $secretArray | Table -Columns VaultName,Name,SecretValue,Created,Updated -Headers 'VaultName','Name','Value','Created','Updated' -Width 0
    }

     Section -Style Heading1 'Secret RBAC Details' {


            Paragraph -Style Heading2 " RBAC Role Assignments " -Font 'Arial' -Size 10        
            $secret_role_assignments | Table -Columns Secret,Vault,DisplayName,ObjectType,RoleDefinitionName -ColumnWidths 10,15,20,15,40 -Headers 'Secret','Vault','User','User Type','RBAC Role'
            PageBreak 
            
        }
        Section -Style Heading1 'Groups' {
            Paragraph -Style Heading2 " Group Members" -Font 'Arial' -Size 10        
            $members | Table -Columns DisplayName,Group,ObjectType -Headers 'User','Group','User Type' -Width 0

        }    
} # End Document Script Block

<# Generate #>
$document | Export-Document -Path $Path -Format:$Format

} # End Generate Report. End function