CloudEval Cloud sync reads Azure Resource Manager metadata, exports resource-group templates for analysis, and stores a project snapshot. It does not need Contributor for normal sync.
Recommended role
Create a custom role scoped to each resource group you want CloudEval to evaluate.
| Permission | Why CloudEval needs it |
|---|
*/read | Read subscription, resource group, resource, provider, deployment, and topology metadata inside the assigned scope. |
Microsoft.Resources/deployments/exportTemplate/action | Export deployment templates used to build report inputs from live Azure resources. |
Microsoft.Network/networkWatchers/topology/action | Read Network Watcher topology relationships used to enrich the live architecture graph. |
| Data actions | None. CloudEval Cloud sync does not need data-plane access such as storage blob contents or Key Vault secret values. |
Create the service principal and role
Run this from an Azure CLI session that can create app registrations and role definitions in the target subscription. The command also uses jq to extract the generated service principal fields. Replace RG with the resource group CloudEval should evaluate.
SUB_ID="$(az account show --query id -o tsv)"
RG="<target-resource-group>"
NW_RG="NetworkWatcherRG"
SP_NAME="cloudeval-live-sync"
ROLE_NAME="CloudEval Live Sync Reader"
SCOPE="/subscriptions/$SUB_ID/resourceGroups/$RG"
NW_SCOPE="/subscriptions/$SUB_ID/resourceGroups/$NW_RG"
SP_JSON="$(
az ad sp create-for-rbac \
--name "$SP_NAME" \
--skip-assignment \
-o json
)"
APP_ID="$(echo "$SP_JSON" | jq -r .appId)"
CLIENT_SECRET="$(echo "$SP_JSON" | jq -r .password)"
TENANT_ID="$(echo "$SP_JSON" | jq -r .tenant)"
cat > cloudeval-live-sync-reader.json <<EOF
{
"Name": "$ROLE_NAME",
"IsCustom": true,
"Description": "Read Azure resources and export deployment templates for CloudEval Cloud sync.",
"Actions": [
"*/read",
"Microsoft.Resources/deployments/exportTemplate/action",
"Microsoft.Network/networkWatchers/topology/action"
],
"NotActions": [],
"DataActions": [],
"NotDataActions": [],
"AssignableScopes": [
"/subscriptions/$SUB_ID"
]
}
EOF
az role definition create --role-definition cloudeval-live-sync-reader.json
az role assignment create \
--assignee "$APP_ID" \
--role "$ROLE_NAME" \
--scope "$SCOPE"
if az group show --name "$NW_RG" >/dev/null 2>&1; then
az role assignment create \
--assignee "$APP_ID" \
--role "$ROLE_NAME" \
--scope "$NW_SCOPE"
else
echo "Skipped Network Watcher topology assignment: resource group '$NW_RG' was not found."
fi
echo "Client ID: $APP_ID"
echo "Client Secret: $CLIENT_SECRET"
echo "Tenant ID: $TENANT_ID"
echo "Subscription ID: $SUB_ID"
echo "Resource Group Scope: $RG"
Use the printed client ID, client secret, tenant ID, subscription ID, and
resource group in the CloudEval Cloud sync connection form or CLI:
cloudeval projects create \
--cloud-sync \
--azure-tenant-id "$TENANT_ID" \
--azure-client-id "$APP_ID" \
--azure-client-secret "$CLIENT_SECRET" \
--azure-subscription-id "$SUB_ID" \
--resource-group "$RG" \
--name "Cloud sync $RG" \
--format json \
--output ./cloudeval-project.json
If the role definition already exists and you are changing assignable scopes,
use az role definition update with the same JSON.
For multiple resource groups, repeat the target resource-group az role assignment create step for each group and select the same groups in the CloudEval connection form.
The NetworkWatcherRG assignment is needed only when CloudEval should enrich the graph with Network Watcher topology and the Network Watcher resource lives outside the target resource group. If your tenant uses a different Network Watcher resource group, replace NW_RG. If that assignment is omitted, Cloud sync can still read resources and export templates, but Network Watcher relationship enrichment may be incomplete.
When subscription scope is acceptable
Resource-group scope is the default recommendation. Use subscription scope only when CloudEval should discover and sync every accessible resource group in the subscription.
az role assignment create \
--assignee "$APP_ID" \
--role "CloudEval Live Sync Reader" \
--scope "/subscriptions/$SUB_ID"
Do not use Contributor for production sync
This smoke-test command is intentionally broader than CloudEval needs:
az ad sp create-for-rbac \
--name cloudeval-sync-smoke \
--role Contributor \
--scopes "/subscriptions/$SUB_ID/resourceGroups/$RG"
Use it only for disposable validation in a throwaway resource group. Replace it with the custom role above before connecting real environments.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|
| Connection test authenticates but no resource groups appear | Role assignment has not propagated or is scoped to a different subscription/resource group | Wait a few minutes, verify the assignment scope, then test again. |
| Sync sees the resource group but reports fail to build | The custom role is missing Microsoft.Resources/deployments/exportTemplate/action | Update the role definition and rerun sync. |
| Graph sync works but network relationships are sparse | The custom role is missing Microsoft.Network/networkWatchers/topology/action or lacks assignment on the Network Watcher resource group | Add the topology action and assign the role on NetworkWatcherRG or the subscription scope used for Network Watcher. |
| Sync unexpectedly includes too many resource groups | The role is assigned at subscription scope or the CloudEval resource-group list is empty | Assign at resource-group scope and select target resource groups in CloudEval. |
| Key Vault secret values or storage blob contents are expected in reports | Cloud sync uses management-plane metadata only | Do not add data actions unless a separate workflow explicitly requires them. |
Last modified on May 22, 2026