Skip to main content

Managing Azure IAM Roles and Permissions

Overview

Understanding Azure Identity and Access Management (IAM) roles and permissions is crucial for developer teams to securely and efficiently manage access to cloud resources while maintaining the least privilege principles.

In order to ensure a clear separation of concerns and resource ownership, as DX team, we have defined a set of guidelines and best practices for managing IAM roles and permissions in Azure.

Roles and Permissions

We propose a role-based access control (RBAC) model that leverages Azure Entra ID groups to manage access to Azure resources. Each team will have three Entra ID security groups:

  • Admins (domain experts)
  • Developers (regular team members)
  • Externals (contractors)

This segmentation allows for appropriate access levels based on role responsibilities and trust levels. Each team is responsible and autonomous on how to split members among these groups by making PRs on eng-azure-authorization repository.

This section outlines the IAM configuration for each Entra ID group.

Subscription-Level Access

Admin Group

  • Contributor role on the subscription: can view and manage all resources
  • Can assist in emergency situations and manage cross-team resources

Developer Group

  • Reader role on the subscription: can view all resources but cannot modify them
  • Provides an overview of the broader infrastructure context

External Group

  • Reader role on the subscription (same as Developer group)
  • Can understand the system architecture while maintaining security

Resource Group-Level Access

Team-Specific Resource Groups

While subscriptions may be shared across teams, resource groups are owned and managed by a single team.

Admin Group
  • Owner role: full control over all resources within the group
  • Can manage IAM assignments within the group
  • Can manage resource locks
  • In Key Vaults using RBAC:
    • Key Vault Data Access Administrator role: can manage secrets, keys and certificates
    • Key Vault Administrator role: can manage resource properties
Developer Group
  • Contributor role: can create and manage resources
  • Cannot modify IAM assignments
  • Cannot modify resource locks
  • In Key Vaults using RBAC:
    • Key Vault Secrets Officer role: can manage secrets
External Group
  • Reader (inherited) role: can view resources and configurations
  • No modification privileges

Shared Resources-Level Access

Some resources are inherently shared across teams due to their nature or architectural decisions. These include (but are not limited to):

  • Application Gateways
  • API Management
  • Public and Private DNS Zones
  • Private Endpoints
  • Other product-specific resources

For shared resources, teams must request role assignments through Pull Requests to the repository where they are defined (generally <product-name>-infra). This process is useful to control access and management of critical resources with big radius impact.

Role-Based Access Matrix

The following roles are set up automatically by the Terraform module azure_github_environment_bootstrap. Its usage is strong advised for mono repositories. The first view is cleaner and easier to read, recommended for developers.

Simplified view
Product SubscriptionProduct Private EndpointsProduct Private DNS ZoneProduct NAT GatewayProduct APIMTeam Resource GroupsOpex Resource Group
Entra ID AdmWriterWriterWriterWriterWriterWriter - Lock manager - IAM managerWriter
Entra ID DevsReaderWriterReaderReaderReaderWriter - No access to Certificate and KeysReader
Entra ID ExtReaderWriterReaderReaderReaderReaderReader
****
ID Infra CIReaderReaderReaderReaderList secretsReaderReader
ID Infra CDReader - IAM ManagerWriterWriterWriterWriterWriter - IAM Manager - Manage resource locksReader - IAM Manager
ID App CDReaderWriterWriterWriterWriterWriterReader
ID Opex CIReaderReaderReaderReaderReaderReaderReader
ID Opex CDReaderReaderReaderReaderReaderReaderWriter

This view is instead recommended for cloud operators.

Detailed view
Product SubscriptionProduct Private EndpointsProduct Private DNS ZoneProduct NAT GatewayProduct APIMTeam Resource GroupsOpex Resource Group
Entra ID AdmContributor(inherited Contributor)(inherited Contributor)(inherited Contributor)(inherited Contributor)Owner - Key Vault Data Access Administrator - Key Vault Administrator(inherited Contributor)
Entra ID DevsReader(inherited Reader)(inherited Reader)(inherited Reader)(inherited Reader)Contributor - Key Vault Secrets Officer(inherited Reader)
Entra ID ExtReader(inherited Reader)(inherited Reader)(inherited Reader)(inherited Reader)Reader(inherited Reader)
****
ID Infra CIReader - Reader and Data Access - PagoPA Iac Reader - DocumentDB Account Contributor(inherited Reader - Reader and Data Access - PagoPA Iac Reader - DocumentDB Account Contributor)(inherited Reader - Reader and Data Access - PagoPA Iac Reader - DocumentDB Account Contributor)(inherited Reader - Reader and Data Access - PagoPA Iac Reader - DocumentDB Account Contributor)PagoPA API Management Service List SecretsKey Vault Secrets User - Key Vault Certificate User - Key Vault Crypto User - Storage Blob Data Reader - Storage Queue Data Reader - Storage Table Data Reader(inherited Reader - Reader and Data Access - PagoPA Iac Reader - DocumentDB Account Contributor)
ID Infra CDReader - Role Based Access Control AdministratorNetwork ContributorPrivate DNS Zone ContributorNetwork ContributorAPI Management Service ContributorContributor - Key Vault Secrets Officer - Key Vault Certificates Officer - Key Vault Crypto Officer(inherited Reader - Role Based Access Control Administrator)
ID App CDReader(inherited Reader)(inherited Reader)(inherited Reader)(inherited Reader)(inherited Reader)Contributor
ID Opex CIReader - Reader and Data Access(inherited Reader - Reader and Data Access)(inherited Reader - Reader and Data Access)(inherited Reader - Reader and Data Access)(inherited Reader - Reader and Data Access)(inherited Reader - Reader and Data Access)(inherited Reader - Reader and Data Access)
ID Opex CDReader(inherited Reader - Reader and Data Access)(inherited Reader - Reader and Data Access)(inherited Reader - Reader and Data Access)(inherited Reader - Reader and Data Access)(inherited Reader - Reader and Data Access)Monitoring Contributor - PagoPA Opex Dashboards Contributor

Roles explanation

RoleDescription
ReaderResource control plane read-only access
Reader and Data AccessResource data plane read access. Allows writing on Storage Account using keys as authentication system
PagoPA IaC ReaderList keys and credentials for IaC access
DocumentDB Account ContributorCosmosDB control plane access
Key Vault Data Access AdministratorManage access to Azure Key Vault
Key Vault AdministratorIncludes data plane operations on a key vault and all objects in it, including certificates, keys, and secrets
Key Vault Secrets OfficerPerform any action on the secrets of a key vault, except managing permissions
Role Based Access Control AdministratorManage access to Azure resources by assigning roles using Azure RBAC
User Access AdministratorLets you manage user access to Azure resources, including locks
Network ContributorLets you manage networks, but not access to them
Private DNS Zone ContributorLets you manage private DNS zone resources, but not the virtual networks they are linked to
API Management Service ContributorCan manage the service and the APIs
Storage Blob Data ContributorRead, write, and delete Azure Storage containers and blobs
Storage Queue Data ContributorRead, write, and delete Azure Storage queues and queue messages
Storage Table Data ContributorAllows for read, write, and delete access to Azure Storage tables and entities

Code Examples (How-To) and Common Troubleshooting

How to label a resource group as team-owned

Create the resource group in Repository Terraform configuration of your mono repository and pass its resource id to the azure_github_environment_bootstrap module via the additional_resource_group_ids variable. All roles will be automatically applied.

Example
  resource "azurerm_resource_group" "domain_itn_01" {
name = "name"
location = "location"
tags = local.tags
}

module "repo" {
source = "pagopa-dx/azure-github-environment-bootstrap/azurerm"
version = "~>1.0"

TBD = [
azurerm_resource_group.domain_itn_01.id
]
}

Azure Resources

Understand IAM and assigning the right role in the right way could be difficult. So, this section shows most common scenarios developers may face.

Notes

The label set by the module means that the role is already given by the DX module azure_github_environment_bootstrap to Entra ID groups. Applications still need a role assignment (possibly with the narrowest scope).

Code samples should be set next to the resource definition, unless otherwise specified. The repository can be found in resource tags on Azure Portal.

Writer role includes Reader, unless otherwise specified.

Azure APIM

If you get an error about roles while operating on APIM, please add the apim_id optional variable to the azure_github_environment_bootstrap by specifying the APIM instance resource Id.

Azure Cache for Redis

Team-owned Redis

Set by the module

Shared Redis

Example
resource "azurerm_role_assignment" "" {
scope = <redis-id>
role_definition_name = "Contributor"
principal_id = <who-needs-access>
}

Azure Cosmos DB

In both scenarios, it is mandatory to manually set IAM roles. This approach is recommended as keys are not secure.

It is also recommended to set roles at container level rather than Cosmos DB Account, especially for applications and shared Accounts.

Example
module "" {
source = "pagopa-dx/azure-role-assignments/azurerm"
version = "~>0.0"

principal_id = <who-needs-access>

cosmos = [
{
account_name = <st-target-name>
resource_group_name = <st-target-rg-name>
role = "reader" # or writer
}
]
}

Optionally set the database and container via optional variables database and collections.

Azure Event Hub

Team-owned Event Hub

Set by the module

Shared Event Hub

Example
module "" {
source = "pagopa-dx/azure-role-assignments/azurerm"
version = "~>0.0"

principal_id = <who-needs-access>

event_hub = [
{
namespace_name = <st-target-name>
resource_group_name = <st-target-rg-name>
role = "reader" # or writer
}
]
}

Optionally set the Event Hubs via event_hub_names property.

Azure Key Vault

Team-owned Key Vault

Set by the module if using RBAC access model (recommended).

Enabling RBAC access model to Key Vault
resource "azurerm_key_vault" "" {
name = "name"
location = "location"
resource_group_name = "resource-group"

enable_rbac_authorization = true
}

Otherwise via Access Policies:

Manage Key Vault Access Policies
module "" {
source = "pagopa-dx/azure-role-assignments/azurerm"
version = "~>0.0"

principal_id = <who-needs-access>

key_vault = [
{
secrets = "writer" # or writer or owner
certificates = "writer" # or writer or owner
keys = "writer" # or writer or owner
}
]
}

Shared Key Vault

Example
module "" {
source = "pagopa-dx/azure-role-assignments/azurerm"
version = "~>0.0"

principal_id = <who-needs-access>

key_vault = [
{
secrets = "writer" # or writer or owner
certificates = "writer" # or writer or owner
keys = "writer" # or writer or owner
}
]
}

Azure NAT Gateway

If you get an error about roles while associating a NAT Gateway to an AppService/Function App, please add the nat_gateway_resource_group_id optional variable to the azure_github_environment_bootstrap by specifying the NAT Gateways' resource group Id.

Azure Notification Hub

Team-owned Notification Hub

Set by the module

Shared Notification Hub

Enable Terraform state access via Entra ID
resource "azurerm_role_assignment" "" {
scope = <notification-hub-namespace-id>
role_definition_name = "Contributor"
principal_id = <who-needs-access>
}

Azure Storage Account

It is recommended to use Entra ID authentication to access Storage Accounts, instead of using keys. In fact, key-based access ignores IAM roles and can be used by anyone without further authentication.

To set Entra ID authentication as default and disabling key access, set the properties TBD of the Storage Account DX module.

Enable Terraform state access via Entra ID
backend "azurerm" {
...
`use_azuread_auth` = true
}
Enable Storage Account access via Entra ID
provider "azurerm" {
features {}
storage_use_azuread = true
}

Blob

Team-owned Storage Account

Set by the module

Shared Storage Account
Example
module "" {
source = "pagopa-dx/azure-role-assignments/azurerm"
version = "~>0.0"

principal_id = <who-needs-access>

storage_blob = [
{
storage_account_name = <st-target-name>
resource_group_name = <st-target-rg-name>
role = "reader" # or writer
}
]
}

Optionally set the container name via container_name property.

Terraform Storage Account

Set by the module

Queue

Team-owned Storage Account

Set by the module

Shared Storage Account
Example
module "" {
source = "pagopa-dx/azure-role-assignments/azurerm"
version = "~>0.0"

principal_id = <who-needs-access>

storage_queue = [
{
storage_account_name = <st-target-name>
resource_group_name = <st-target-rg-name>
role = "reader" # or writer
}
]
}

Optionally set the container name via queue_name property.

Terraform Storage Account

Set by the module

Table

Team-owned Storage Account

Set by the module

Shared Storage Account
Example
module "" {
source = "pagopa-dx/azure-role-assignments/azurerm"
version = "~>0.0"

principal_id = <who-needs-access>

storage_table = [
{
storage_account_name = <st-target-name>
resource_group_name = <st-target-rg-name>
role = "reader" # or writer
}
]
}

Optionally set the table name via `table_name` property.

Terraform Storage Account

Set by the module

Best Practices

Role Usage Guidelines

  • Follow least-privilege principle
  • Add a brief description in the proper field of the role assignment object when available
  • Use admin privileges only for critical operations

Security Considerations

  • Avoid connection strings; use managed identities where possible
  • Remove unused role assignments