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:
- Get all the secrets from each vault I have access to in the tenant.
- For each secret derive the secret scope string
- For each scope string get the associated role assignments
- For each role assigned to a group, get the group members
- Using Pscribo comandlets create a document structure for the collected data
- 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