Azure Permissions Required¶
CertifyClouds requires specific Azure permissions to discover and scan Key Vaults across your subscriptions and to inventory App Registrations and Enterprise Applications. This document outlines the minimum required permissions for secure operation.
Quick Summary¶
Minimum Required Access: Reader at the Subscription scope, plus Key Vault Reader on each vault (or List on secrets, certificates and keys plus Get on certificates for access-policy vaults)
What CertifyClouds Can Access:
- List subscriptions
- List Key Vaults
- Read Key Vault configuration (network, policies, RBAC)
- Read secret/key/certificate metadata (names, expiry dates)
- Read the certificates Azure services serve on their TLS endpoints (details only, never private keys)
- Read App Registration and Enterprise Application credential metadata
What CertifyClouds CANNOT Access with the minimum (Scan) role:
- Secret values
- Private key material
- Certificate private keys
- Modify any resources
- Delete any resources
PRO Rotation and full-stack rotation add narrowly scoped write and certificate-read permissions, detailed below.
For keyless AWS and Google Cloud sync, the optional --sync-federation setup flag creates a separate single-tenant App Registration with an application role for the deployment's managed identity. It creates no client secret or certificate. The operator also needs permission to create App Registrations and service principals and assign that role.
Automated Setup¶
Use the provided setup script to automatically configure Key Vault permissions:
Prerequisites¶
Run the setup script as an operator who is signed in with az login and holds Owner or User Access Administrator on the target subscriptions (required to assign roles). You do not need to pre-grant the managed identity (system-assigned or user-assigned) any role; with --apply, the script grants Reader at the subscription scope (plus the Key Vault roles) to the managed identity for you.
First, get the managed identity's Principal ID to pass to the script:
# Get your Managed Identity's Principal ID
PRINCIPAL_ID=$(az vm show --resource-group <rg> --name <vm> --query identity.principalId -o tsv)
Deployed with the CertifyClouds deploy scripts
The deploy scripts run CertifyClouds as a user-assigned managed identity: id-cc-<region>-<name> in the deployment's resource group, or the one you passed with --user-assigned-identity (see Installation). On a new install the deploy stops before CertifyClouds starts and prints this script's command with the identity's Principal ID already filled in. To look the Principal ID up later:
# The identity the deploy created
PRINCIPAL_ID=$(az identity show --name id-cc-<region>-<name> --resource-group <rg> --query principalId -o tsv)
# An identity you passed with --user-assigned-identity
PRINCIPAL_ID=$(az identity show --ids <identity-resource-id> --query principalId -o tsv)
The same identity pulls the image, so it also needs AcrPull on your Container Registry. The deploy grants it, and stops if it cannot and the identity does not have it yet. If your identities are provisioned centrally, grant it ahead of time.
Script paths
The examples below run from the root of the delivered customer package, where ./setup-certifyclouds-access.sh is.
Least-privilege operators
If the operator running the script lacks role-assignment rights, the script warns and continues rather than failing, because it cannot grant Reader itself. In that case, have someone with Owner or User Access Administrator grant Reader to the managed identity on each subscription beforehand:
Run the Setup Script¶
On Windows, read Running from Windows before running the script.
# 1. Check current permissions (dry run)
./setup-certifyclouds-access.sh --principal-id $PRINCIPAL_ID
# 2. Apply read permissions for Key Vault scanning
./setup-certifyclouds-access.sh --principal-id $PRINCIPAL_ID --apply
# 3. (Optional) Enable Rotation write permissions (PRO tier)
./setup-certifyclouds-access.sh --principal-id $PRINCIPAL_ID --apply --with-rotation
# 3b. (Optional) Also allow full-stack rotation to update the resources that
# consume a credential (App Service / SQL / Container Apps / Logic Apps)
./setup-certifyclouds-access.sh --principal-id $PRINCIPAL_ID --apply --with-rotation --with-resource-updates
# 4. (Optional) Add subnet to Key Vault firewalls
./setup-certifyclouds-access.sh --principal-id $PRINCIPAL_ID --apply --subnet-id /subscriptions/.../subnets/snet-certifyclouds
# 5. (Optional) Let CertifyClouds read app owners' email addresses (owner emails)
./setup-certifyclouds-access.sh --principal-id $PRINCIPAL_ID --owner-notifications --apply
# 6. (Optional) Let CertifyClouds read the tenant's app management policy
# (compliance rules AMP001-AMP003)
./setup-certifyclouds-access.sh --principal-id $PRINCIPAL_ID --app-policy-read --apply
The script will:
- Discover all subscriptions visible to your signed-in
azsession (narrow with--subscription <id>) - Find all Key Vaults across those subscriptions
- Auto-detect whether each vault uses RBAC or Access Policies
- Check existing permissions and report any gaps
- Apply missing permissions when
--applyis specified
Authentication Methods¶
1. Managed Identity (Recommended)¶
Best for: Azure VMs, Container Instances, AKS, Container Apps
# Enable system-assigned managed identity on your VM
az vm identity assign \
--resource-group rg-certifyclouds \
--name vm-certifyclouds
# Get the identity principal ID
IDENTITY_ID=$(az vm show \
--resource-group rg-certifyclouds \
--name vm-certifyclouds \
--query identity.principalId -o tsv)
# Assign Reader role at subscription scope
az role assignment create \
--assignee $IDENTITY_ID \
--role Reader \
--scope /subscriptions/{subscription-id}
Configuration:
Benefits:
- No credentials to manage or rotate
- More secure than service principals
- Automatic credential refresh
- Audit trail in Azure Activity Log
2. Service Principal¶
Best for: On-premises deployment, non-Azure environments
# Create service principal
az ad sp create-for-rbac \
--name "CertifyClouds Platform Workload (Production)" \
--role Reader \
--scopes /subscriptions/{subscription-id}
Configuration:
AZURE_CLIENT_ID=12345678-1234-1234-1234-123456789abc
AZURE_CLIENT_SECRET=your-client-secret
AZURE_TENANT_ID=87654321-4321-4321-4321-cba987654321
Security Best Practice
- Store client secret in Azure Key Vault (not in config files)
- Rotate client secret every 90 days
- Use certificate-based auth for production
- Enable MFA for the service principal
Multi-Subscription Access¶
To scan multiple Azure subscriptions, grant the Reader role on each subscription:
Per-Subscription Assignment¶
# Assign to subscription 1
az role assignment create \
--assignee $IDENTITY_ID \
--role Reader \
--scope /subscriptions/11111111-1111-1111-1111-111111111111
# Assign to subscription 2
az role assignment create \
--assignee $IDENTITY_ID \
--role Reader \
--scope /subscriptions/22222222-2222-2222-2222-222222222222
Management Group Assignment (Enterprise)¶
For organisations with many subscriptions:
# Assign Reader at management group scope
az role assignment create \
--assignee $IDENTITY_ID \
--role Reader \
--scope /providers/Microsoft.Management/managementGroups/{mg-id}
This grants access to all subscriptions under the management group.
Azure RBAC Permissions Detail¶
Core Permissions (All Tiers)¶
Microsoft.KeyVault/vaults/read
Microsoft.KeyVault/vaults/accessPolicies/read
Microsoft.Resources/subscriptions/read
Microsoft.Resources/subscriptions/resourceGroups/read
Microsoft.Network/privateEndpoints/read
Microsoft.Network/networkSecurityGroups/read
Microsoft.Insights/diagnosticSettings/read
TLS Endpoint Discovery (All Tiers)¶
Each scan reads the certificates your Azure services serve on their TLS endpoints. The Reader role on the subscription already includes every one of these, so no new grant is needed:
Microsoft.Web/sites/read # App Service
Microsoft.Web/certificates/read
Microsoft.Network/applicationGateways/read # Application Gateway
Microsoft.ApiManagement/service/read # API Management
Microsoft.App/containerApps/read # Container Apps
Microsoft.App/managedEnvironments/certificates/read
Microsoft.Network/frontDoors/read # Front Door (classic)
Microsoft.Cdn/profiles/read # Front Door Standard / Premium
Microsoft.Cdn/profiles/customDomains/read
Microsoft.Cdn/profiles/secrets/read
Grant Reader on the whole subscription. If it is granted only on resource groups, CertifyClouds cannot confirm it from the subscription, so the TLS Endpoints tab keeps a "could not be fully read" notice for that subscription. See TLS Endpoints.
Dependency Discovery (PRO Tier)¶
Microsoft.Web/sites/read # App Services, Function Apps
Microsoft.Web/sites/config/read # App settings, connection strings
Microsoft.Logic/workflows/read # Logic Apps
Microsoft.Sql/servers/read # SQL Servers
Microsoft.App/containerApps/read # Container Apps
Microsoft.ApiManagement/service/read # API Management
Microsoft.DataFactory/factories/read # Data Factory
Microsoft.DataFactory/factories/linkedservices/read # Data Factory linked services
Microsoft.ContainerService/managedClusters/read # AKS clusters
Microsoft.ServiceBus/namespaces/read # Service Bus
Microsoft.ServiceBus/namespaces/authorizationRules/read # Service Bus auth rules
Microsoft.EventHub/namespaces/read # Event Hubs
Microsoft.EventHub/namespaces/authorizationRules/read # Event Hubs auth rules
Microsoft.Storage/storageAccounts/read # Storage Accounts
Rotation Feature - Additional Permissions (PRO + ENTERPRISE)¶
The Rotation feature requires write access to rotate App Registration credentials and update secret values in Key Vaults.
Microsoft Graph API Permissions¶
| Permission | Tier | Purpose | Type |
|---|---|---|---|
Application.Read.All | STARTER | Read-only - list App Registrations and Enterprise Applications (service principals), including credential metadata, for discovery / inventory views | Application |
Application.ReadWrite.All | PRO | Read App Registrations and add / remove their secrets + certificates during rotation | Application |
User.ReadBasic.All | Optional, all tiers | Show App Registration owner names and email addresses, and let CertifyClouds email owners about their apps once an admin has chosen to (see App Owner Emails). Without it CertifyClouds still counts owners but cannot name them. Off by default because it exposes personal data. The setup script grants it with --owner-notifications | Application |
Policy.Read.All | Optional, all tiers | Read the tenant's app management policy, its limits on App Registration secrets and certificates, for compliance rules AMP001-AMP003. Without it those rules show as not evaluated (see App Registration rules). The setup script grants it with --app-policy-read; for an onboarded corporate tenant, run onboard-corporate-tenant.sh with --app-policy-read | Application |
Grant one of the first two according to your tier; Application.ReadWrite.All is a strict superset, so don't assign both. Add User.ReadBasic.All only if you want owner names shown or owners emailed, and Policy.Read.All only if you want the app management policy rules. Settings → Access shows which of these each identity holds; see Checking access in CertifyClouds.
Service principal: add the permissions under API permissions in the Azure Portal, then grant admin consent.
Managed identity: a managed identity has no API permissions page in the portal. Assign the Graph app role directly with the commands below (needs an account that can consent on behalf of the tenant, for example Global Administrator or Privileged Role Administrator):
# Object ID of the managed identity (from the Container App / VM identity blade)
IDENTITY_OBJECT_ID=<managed-identity-object-id>
# Object ID of the Microsoft Graph service principal in your tenant
GRAPH_SP=$(az ad sp list --filter "appId eq '00000003-0000-0000-c000-000000000000'" --query "[0].id" -o tsv)
# Pick the role IDs you need
# Application.Read.All 9a5d68dd-52b0-4cc2-bd40-abcf44ac3a30
# Application.ReadWrite.All 1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9
# User.ReadBasic.All 97235f07-e226-4f63-ace3-39588e11d3a1
# Policy.Read.All 246dd0d5-5bd0-4def-940b-0421030a5b68
for ROLE in 1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9 97235f07-e226-4f63-ace3-39588e11d3a1; do
az rest --method POST \
--url "https://graph.microsoft.com/v1.0/servicePrincipals/$IDENTITY_OBJECT_ID/appRoleAssignments" \
--body "{\"principalId\":\"$IDENTITY_OBJECT_ID\",\"resourceId\":\"$GRAPH_SP\",\"appRoleId\":\"$ROLE\"}"
done
No separate admin consent step is needed for a managed identity; the assignment itself is the consent. Then open Settings → Access in CertifyClouds and click Refresh Permissions on the home tenant's row.
Application.Read.All already covers reading /servicePrincipals with Application and Legacy types, including keyCredentials and passwordCredentials. Enterprise Application monitoring therefore requires no new consent. CertifyClouds counts every owner object Microsoft Graph returns, including service-principal owners. Naming those owners needs the optional User.ReadBasic.All permission from the table above.
Applying a new permission to a running install
Granted or changed a Microsoft Graph permission after CertifyClouds was already running? Open Settings → Access and click Refresh Permissions on the identity's row. For the home tenant, CertifyClouds drops its cached Graph token and asks Azure for a new one. Refresh Permissions and Test Connection are admin-only.
With a service principal the new permission takes effect straight away. With a managed identity Azure itself caches the identity's token for up to 24 hours and keeps handing back the old one, and restarting the container does not renew it. The new permission shows up once that token expires. Test Connection checks whether the current token can read App Registrations.
Key Vault data-plane write¶
Rotation needs to write new secret values into target Key Vaults. Use the data-plane RBAC role rather than the control-plane Contributor:
az role assignment create \
--assignee $IDENTITY_ID \
--role "Key Vault Secrets Officer" \
--scope /subscriptions/{subscription-id}
Key Vault Secrets Officer grants set / get / list / delete on secret values inside the vault. Use Key Vault Certificates Officer for certificate rotation. During certificate rotation CertifyClouds also reads the certificate's backing secret (the certificate with its private key) via Key Vault Secrets User, then re-imports the rotated certificate; it never leaves your tenant.
When the certificate also has copies in other vaults, CertifyClouds updates each copy and its certificate policy, so the copies stay in step with the original. That needs the certificates/update permission (Update Certificate Policy). On vaults that use Azure RBAC, Key Vault Certificates Officer and Key Vault Administrator grant it and Key Vault Certificate User does not (Key Vault built-in roles). On vaults that use access policies, grant Get, Import and Update on certificates; the setup script's --with-rotation grants all three. Without the update permission the copy still gets the new certificate, but the rotation run records the copy's policy update as failed.
The Crypto roles are used only by the KV→KV key replication sub-feature of Sync: Key Vault Crypto User on the source vault to export a key (backup_key) and Key Vault Crypto Officer on the target vault to import it (restore_key_backup). CertifyClouds does not use a Key Vault key for its own encryption, so grant the Crypto roles only when you enable KV→KV key replication. The setup script grants Key Vault Crypto Officer per vault on the --with-rotation path.
Avoid Key Vault Contributor
The Key Vault Contributor role is a control-plane role that permits deleting vaults, modifying access policies, and changing network rules - far broader than rotation requires. Use the least-privilege data-plane Officer roles above instead. Grant Contributor only if a customer ops policy explicitly requires it.
Dependent-resource write (full-stack rotation, PRO Dependencies)¶
Full-stack rotation writes the rotated credential into the dependent resources that consume it, so it needs control-plane WRITE on those resource types. The resource updaters touch:
Microsoft.Web/sites/write # App Services, Function Apps (Website Contributor)
Microsoft.Sql/servers/write # SQL Servers (SQL Server Contributor)
Microsoft.App/containerApps/write # Container Apps (Container Apps Contributor)
Microsoft.Logic/workflows/write # Logic Apps (Logic App Contributor)
Each write is covered by a least-privilege built-in role: Website Contributor (App Services / Functions), SQL Server Contributor (SQL admin password), Container Apps Contributor (Container App secrets), and Logic App Contributor (Logic App workflows). Assign these at subscription scope, and only when full-stack rotation is enabled for those dependents. The setup script grants all four on the --with-resource-updates path.
az role assignment create \
--assignee $IDENTITY_ID \
--role "Website Contributor" \
--scope /subscriptions/{subscription-id}
az role assignment create \
--assignee $IDENTITY_ID \
--role "SQL Server Contributor" \
--scope /subscriptions/{subscription-id}
az role assignment create \
--assignee $IDENTITY_ID \
--role "Container Apps Contributor" \
--scope /subscriptions/{subscription-id}
az role assignment create \
--assignee $IDENTITY_ID \
--role "Logic App Contributor" \
--scope /subscriptions/{subscription-id}
Permission Verification¶
Checking access in CertifyClouds¶
Settings → Access lists every identity CertifyClouds signs in with: the home tenant's always, and each corporate, Azure AD B2C or Entra External ID tenant once you connect it. Only admins can open it. Grants are made in Azure and Microsoft Entra, never here.
| Column | What it shows |
|---|---|
| Where | The tenant |
| Identity | How CertifyClouds signs in there: managed identity or service principal |
| Used for | Scan, Rotate, Sync and Owner emails, and whether each has what it needs |
| Graph permissions | How many of the Microsoft Graph permissions it uses are granted, and which required or optional ones are missing |
| Azure access | The subscriptions where it holds Reader, and how many vaults it can read and write |
| Secret | For a tenant connection, when CertifyClouds' own secret expires and whether it rotates automatically |
| Last checked | When its access was last read |
Open a row for the detail: each Graph permission and what it is for, and for each subscription, every vault's permission model, whether CertifyClouds can read it, write secrets and import certificates, and which role or access policy grants that. A grant made through a group shows as Maybe, through a group.
Each row has two buttons:
- Refresh Permissions reads the identity's Graph permissions and Azure access again. Use it after changing a grant in Azure or Microsoft Entra. For the home tenant, a permission you have just granted takes effect straight away.
- Test Connection checks that the identity works now. For the home tenant it checks three things: CertifyClouds can sign in, it can see subscriptions, and it can read App Registrations through Microsoft Graph. For a tenant connection it reports a single result.
A summary at the top says whether every identity has the access it needs and lists what needs attention. On a tenant's own page, admins see the same detail in an Identity & access card.
Test Azure Sign-in¶
# For Managed Identity, CertifyClouds auto-detects managed identity
# when deployed to Azure VM, App Service, or Container Apps.
# No manual token retrieval required.
# For Service Principal
az login --service-principal \
--username $AZURE_CLIENT_ID \
--password $AZURE_CLIENT_SECRET \
--tenant $AZURE_TENANT_ID
Test Subscription Access¶
Test Key Vault Access¶
Troubleshooting¶
"Insufficient permissions to list subscriptions"¶
Cause: Managed identity doesn't have Reader on subscription.
Fix:
az role assignment create \
--assignee $IDENTITY_ID \
--role Reader \
--scope /subscriptions/{subscription-id}
"Cannot access Key Vault: {vault-name}"¶
Cause: Key Vault has firewall enabled and CertifyClouds IP is not whitelisted.
Fix:
"AADSTS700016 - Application not found"¶
Cause: Service principal client secret expired or invalid.
Fix:
Security & Privacy¶
The Scan Does NOT Access Vault Contents¶
With only the Scan (Reader + Key Vault Reader) roles, CertifyClouds reads configuration metadata only. It does not access:
- Secret values
- Key material
- Certificate private keys
Why? Reader on the subscription lists vaults and reads their settings but grants no access inside a vault. The Scan uses Key Vault list calls to read secret, certificate and key properties, and Key Vault Reader covers those calls. It cannot read secret values or use keys. Vaults that use access policies need List on secrets, certificates and keys, plus Get on certificates.
PRO rotation is the one exception: when rotating a certificate it reads the certificate's backing secret (the certificate with its private key) inside your tenant via Key Vault Secrets User, solely to re-import the rotated certificate. That value never leaves your environment.
Audit Trail¶
All CertifyClouds API calls are logged in Azure Activity Logs:
# View CertifyClouds activity
az monitor activity-log list \
--caller $IDENTITY_ID \
--start-time 2025-11-09T00:00:00Z
Summary Checklist¶
Before running CertifyClouds scans, ensure:
- [ ] Managed Identity or Service Principal created
- [ ]
Readerrole assigned at subscription scope - [ ]
Key Vault Readerassigned on the vaults (or List on secrets, certificates and keys plus Get on certificates for vaults that use access policies) - [ ] Role assignment verified with
az role assignment list - [ ] CertifyClouds can authenticate (Test Connection in Settings → Access)
- [ ] Firewall rules configured if Key Vaults have network restrictions
- [ ] Audit logging enabled to monitor access
Minimum Required: Reader at /subscriptions/{subscription-id} scope, plus Key Vault Reader on each vault (or List on secrets, certificates and keys plus Get on certificates for access-policy vaults)
Without Rotation, CertifyClouds reads metadata only: no secret values, no key material, no write permissions and no privileged roles.
Next: Network Requirements¶
Permissions get the app authorised; networking gets the app reachable. See Network Requirements for the outbound allowlist (licence server, Docker Hub, Graph, Key Vault), the NSG / Azure Firewall snippets, and a diagnostic curl sequence for troubleshooting connectivity.