Skip to Content
AzureRBAC Role Assignment

RBAC Role Assignment

Assigns an Azure RBAC role to a security group across all resource groups matching an environment naming convention.

Naming Convention

Resource groups must follow: rg-<app>-<environment>-<location>-<instance>

Examples: rg-func-dev-uks-01, rg-api-qa-uks-01

Usage

# Assign Reader role (default) to a group for all dev resource groups .\Perms-Add.ps1 -SecurityGroupName "DevTeam" -Environment dev # Assign Contributor role for QA .\Perms-Add.ps1 -SecurityGroupName "QATeam" -Environment qa -RoleDefinitionName "Contributor"

Parameters

  • -SecurityGroupName (required) — Display name of the Entra ID security group
  • -Environment (required) — One of: dev, qa, prod
  • -RoleDefinitionName (optional) — Azure RBAC role, defaults to Reader

How it works

  1. Looks up the security group via Get-AzADGroup, Get-MgGroup, or az ad group list
  2. Finds all resource groups matching the regex ^rg-[^-]+-<environment>-[^-]+-\d+$
  3. Assigns the specified RBAC role to the group on each matching resource group
  4. Skips existing assignments gracefully

Script

[CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$SecurityGroupName, [Parameter(Mandatory = $true)] [ValidateSet("dev", "qa", "prod")] [string]$Environment, [Parameter(Mandatory = $false)] [string]$RoleDefinitionName = "Reader" ) # Naming standard: rg-<app>-<environment>-<location>-<instance> # Examples: rg-func-dev-uks-01, rg-api-dev-uks-01 # This regex targets the environment segment specifically. $resourceGroupRegex = "^rg-[^-]+-$([regex]::Escape($Environment))-[^-]+-\d+$" Write-Host "Using regex: $resourceGroupRegex" Write-Host "Finding security group '$SecurityGroupName'..." $escapedGroupName = $SecurityGroupName.Replace("'", "''") $securityGroupMatches = @() $hasAzAdGroupCmd = [bool](Get-Command -Name Get-AzADGroup -ErrorAction SilentlyContinue) $hasMgGroupCmd = [bool](Get-Command -Name Get-MgGroup -ErrorAction SilentlyContinue) $hasAzRgCmd = [bool](Get-Command -Name Get-AzResourceGroup -ErrorAction SilentlyContinue) $hasAzRoleCmd = [bool](Get-Command -Name New-AzRoleAssignment -ErrorAction SilentlyContinue) $hasAzCli = [bool](Get-Command -Name az -ErrorAction SilentlyContinue) if ($hasAzAdGroupCmd) { $securityGroupMatches = @(Get-AzADGroup -Filter "displayName eq '$escapedGroupName'" -First 2) } elseif ($hasMgGroupCmd) { $securityGroupMatches = @(Get-MgGroup -Filter "displayName eq '$escapedGroupName'" -Top 2) } elseif ($hasAzCli) { $cliGroups = & az ad group list --display-name $SecurityGroupName --query "[].{Id:id,DisplayName:displayName}" -o json 2>$null if ($cliGroups) { $securityGroupMatches = @($cliGroups | ConvertFrom-Json) } } else { throw "No supported group lookup available. Install/import Az.Resources (Get-AzADGroup), Microsoft.Graph.Groups (Get-MgGroup), or Azure CLI (az)." } if (-not $securityGroupMatches -or $securityGroupMatches.Count -eq 0) { Write-Warning "Security group '$SecurityGroupName' was not found." return } if ($securityGroupMatches.Count -gt 1) { Write-Warning "More than one group matched '$SecurityGroupName'. Please use a unique group name." return } $securityGroup = $securityGroupMatches[0] Write-Host "Finding resource groups for environment '$Environment'..." $resourceGroups = @() if ($hasAzRgCmd) { $resourceGroups = @(Get-AzResourceGroup | Where-Object { $_.ResourceGroupName -match $resourceGroupRegex }) } elseif ($hasAzCli) { $cliResourceGroups = & az group list --query "[].name" -o json 2>$null if ($cliResourceGroups) { $resourceGroups = @( ($cliResourceGroups | ConvertFrom-Json) | Where-Object { $_ -match $resourceGroupRegex } | ForEach-Object { [pscustomobject]@{ ResourceGroupName = $_ } } ) } } else { throw "No supported resource group lookup available. Install/import Az.Resources (Get-AzResourceGroup) or Azure CLI (az)." } if (-not $resourceGroups) { Write-Warning "No matching resource groups found for environment '$Environment'." return } Write-Host "Using security group: $($securityGroup.DisplayName)" Write-Host "Found $($resourceGroups.Count) matching resource group(s):" $resourceGroups.ResourceGroupName | Sort-Object | ForEach-Object { Write-Host " - $_" } $subscriptionId = $null if ($hasAzCli) { $subscriptionId = (& az account show --query id -o tsv 2>$null).Trim() if (-not $subscriptionId) { throw "Azure CLI is available but no active subscription is selected. Run 'az login' and 'az account set --subscription <name-or-id>'." } } foreach ($resourceGroup in $resourceGroups) { Write-Host "Assigning '$RoleDefinitionName' to '$($securityGroup.DisplayName)' on RG '$($resourceGroup.ResourceGroupName)'..." if ($hasAzRoleCmd) { try { New-AzRoleAssignment -ObjectId $securityGroup.Id -RoleDefinitionName $RoleDefinitionName -ResourceGroupName $resourceGroup.ResourceGroupName -ErrorAction Stop } catch { if ($_.Exception.Message -match "already exists") { Write-Host " - Skipping existing assignment." } else { Write-Warning " - Failed: $($_.Exception.Message)" } } } elseif ($hasAzCli) { $scope = "/subscriptions/$subscriptionId/resourceGroups/$($resourceGroup.ResourceGroupName)" $cliAssignOutput = & az role assignment create --assignee-object-id $securityGroup.Id --assignee-principal-type Group --role $RoleDefinitionName --scope $scope --only-show-errors -o none 2>&1 if ($LASTEXITCODE -ne 0) { $errorText = "$cliAssignOutput" if ($errorText -match "already exists|exists") { Write-Host " - Skipping existing assignment." } else { Write-Warning " - Failed: $errorText" } } else { Write-Host " - Assigned." } } else { throw "No supported role assignment command available. Install/import Az.Resources (New-AzRoleAssignment) or Azure CLI (az)." } } Write-Host "Done."
Last updated on