Skip to content

Instantly share code, notes, and snippets.

@TeamDman
Last active August 30, 2023 13:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TeamDman/262a14bfaacb8918fc6e6983977b20f5 to your computer and use it in GitHub Desktop.
Save TeamDman/262a14bfaacb8918fc6e6983977b20f5 to your computer and use it in GitHub Desktop.
Find the owners of an Azure resource group by looking at assigned roles, tags, and the activity log
#!/usr/bin/env pwsh
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][string]$ResourceGroup,
[Parameter(Mandatory = $false)][string]$Subscription
);
function Format-TableHighlight($data) {
$data | Format-Table @{
Label = "Name"
Expression =
{
$color = 0;
switch ($_.Type) {
"Group" { $color = 35; }
"ADObject" { $color = 33; }
"User" { $color = 32; }
default { $color = 32; }
}
$e = [char]27;
return "$e[${color}m$($_.Name)${e}[0m";
}
}, Id
}
function Get-OwnersFromTags(
$tags,
$ownerTags = @(
"owner",
"email",
"contact"
)
) {
# Get iterable list of tag keys
$tagNames = $tags `
| Get-Member -Type Properties `
| Select-Object -ExpandProperty Name;
Write-Host "Found $($tagNames.Count) resource group tags";
$owners = @();
foreach ($key in $tagNames) {
$value = $tags.$key;
foreach ($pattern in $ownerTags) {
if ($key -Match $pattern) {
# Write-Host -ForegroundColor "Green" "Found owner key $key=$value";
$owners += [PSCustomObject]@{
Id = "tag - $key"
Name = $value
}
break;
}
}
}
return $owners;
}
function Get-OwnersFromActivityLogs($ResourceGroup) {
$owners = @();
Write-Host -ForegroundColor "Blue" "Querying activity logs";
$callers = az monitor activity-log list `
--subscription $Subscription `
--start-time (Get-Date).AddDays(-89).ToString("yyyy-MM-dd") `
--end-time (Get-Date).ToString("yyyy-MM-dd") `
--resource-group $ResourceGroup `
--max-events 10000 `
--select "caller" `
--query "[].caller" `
--output json `
| ConvertFrom-Json `
| Sort-Object -Unique `
Write-Host "Found $($callers.Count) activity log entries";
$guids = $callers | Where-Object { $_ -NotMatch "@" };
$emails = $callers | Where-Object { $_ -Match "@" };
if ($guids.Count -gt 0) {
Write-Host -ForegroundColor "Blue" "Querying AD objects";
$payload = @{ ids = $guids } `
| ConvertTo-Json -Compress `
| ForEach-Object { $_ -Replace """", "\""" };
$owners += az rest `
--subscription $Subscription `
--method POST `
--url 'https://graph.microsoft.com/v1.0/directoryObjects/getByIds' `
--headers 'Content-Type=application/json' `
--body $payload `
--query "value | [].{Id: join('', ['audit - ', id]), Name: displayName, Type: 'ADObject'}" `
| ConvertFrom-Json;
}
if ($emails.Count -gt 0) {
Write-Host -ForegroundColor "Blue" "Querying AD users";
$payload = $emails `
| ForEach-Object { "userPrincipalName eq '$_'" } `
| Join-String -Separator " or ";
$owners += az ad user list `
--filter $payload `
--query "[].{Id: join('', ['audit - ', userPrincipalName]), Name: mailNickname}" `
| ConvertFrom-Json;
}
return $owners;
}
function Get-OwnersFromRbac($scope) {
$owners = @();
Write-Host -ForegroundColor "Blue" "Querying RBAC roles on scope $scope";
$assignments = az role assignment list `
--subscription $Subscription `
--all `
--query "[? contains(scope, '$scope') ]" `
--output json `
| ConvertFrom-Json;
Write-Host "Found $($assignments.Count) role assignments";
$owners += $assignments `
| ForEach-Object {
$_.scope = $_.scope -replace $scope, "ResourceGroup";
return [PSCustomObject]@{
Id = "rbac - $($_.roleDefinitionName) - $($_.scope) - $($_.principalId)"
Name = $_.principalName
Type = $_.principalType
};
};
$groups = $assignments `
| Where-Object { $_.principalType -eq "Group" } `
| Select-Object -ExpandProperty principalName;
Write-Host "Found $($groups.Count) groups in role assignments";
foreach ($groupName in $groups) {
Write-Host -ForegroundColor "Blue" "Querying members for $groupName";
$groupMembers = az ad group member list `
--group $groupName `
--output json `
| ConvertFrom-Json;
Write-Host -ForegroundColor "Blue" "Querying owners for $groupName";
$groupOwners = az ad group owner list `
--group $groupName `
--output json `
| ConvertFrom-Json;
$owners += $groupMembers `
| ForEach-Object {
return [PSCustomObject]@{
Id = "$groupName - member"
name = "$($_.displayName) - $($_.jobTitle)"
}
};
$owners += $groupOwners `
| ForEach-Object {
return [PSCustomObject]@{
Id = "$groupName - owner"
name = "$($_.displayName) - $($_.jobTitle)"
}
};
}
return $owners;
}
if ([string]::IsNullOrEmpty($Subscription)) {
$Subscription = az account list `
--query "[? isDefault].name" `
--output tsv;
}
Write-Host -ForegroundColor "Blue" "Using subscription $Subscription";
Write-Host -ForegroundColor "Blue" "Querying group";
$group = az group show `
--subscription $Subscription `
--resource-group $ResourceGroup `
--output json `
| ConvertFrom-Json;
if (!$?) {
Write-Error "Could not find group ""$ResourceGroup""";
return;
}
$owners = @();
$owners += Get-OwnersFromTags $group.tags;
$owners += Get-OwnersFromActivityLogs $ResourceGroup;
$owners += Get-OwnersFromRbac $group.id;
if ($owners.Count -gt 0) {
Format-TableHighlight $owners;
}
else {
Write-Error "Found no owners for $ResourceGroup :(";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment