Skip to main content

Azure CSP Tenant Profile Setup

To manage Kubernetes clusters in your Azure subscription, Codiac needs an identity in your Azure tenant: an App Registration called "Codiac Relay." This page explains what it is, why it's needed, exactly what permissions it requires, and provides scripts you can run to set it up.

Background: App Registration vs. Enterprise Application

If you've worked with Azure Entra ID (formerly Azure AD), you've probably seen both "App Registrations" and "Enterprise Applications" in the portal. They are not two separate things you choose between — they are two views of the same object:

Azure Portal ViewMS Graph API ObjectWhat it is
App RegistrationApplicationThe definition of the app — where you configure redirect URIs, API permissions, and client secrets. Think of it as the class.
Enterprise ApplicationServicePrincipalThe instance of the app in a specific tenant — where role assignments and user consent live. Think of it as the object.

When you create an App Registration in the portal, Azure automatically creates a matching Service Principal (Enterprise Application) in your tenant. Through the MS Graph API (which Codiac uses), you must create them explicitly in two steps.

Codiac uses an App Registration because it needs to:

  1. Authenticate users via OAuth2 (authorization code flow) — this requires a redirect URI, which is configured on the App Registration.
  2. Authenticate pipelines via client credentials — this requires a client secret, also configured on the App Registration.
  3. Request delegated API permissions — configured on the App Registration, consented on the Enterprise Application.

There is no scenario where you would use an Enterprise Application instead of an App Registration. You always need the App Registration; the Enterprise Application comes along with it automatically.

How Codiac Uses Permissions

Codiac operates with two distinct identities in your Azure tenant:

Identity 1: The "Codiac Relay" App Registration

This is the platform identity — created once during onboarding. It acts on behalf of the logged-in user (delegated permissions) to orchestrate cluster operations. This is what you are setting up on this page.

Identity 2: Per-Cluster Service Principals

When Codiac creates an AKS cluster, it creates a dedicated Service Principal for that cluster. This SP is the cluster's own identity, used for pulling container images from your ACRs and managing networking resources. Codiac creates these automatically during cluster creation.

Why two identities?

This separation follows the principle of least privilege. The per-cluster SP only has the narrow permissions it needs (e.g., ACR pull access). It cannot create other clusters or modify your Entra directory. If a cluster is compromised, the blast radius is limited to that cluster's own resources.

Least Privilege: Exactly What Codiac Needs

Entra ID (Directory) Roles

The user performing the initial CSP setup must have these Entra ID roles:

Entra RoleWhy
Cloud Application AdministratorCreate and manage App Registrations and Service Principals (for per-cluster identities).
Privileged Role AdministratorGrant admin consent for the API permissions Codiac needs.
warning

These Entra roles are only required for the user who performs the CSP setup and creates clusters. Other users who simply deploy applications through Codiac do not need these roles.

API Permissions (Delegated Scopes)

These are the OAuth2 delegated permissions configured on the Codiac App Registration:

Target ResourceScopeWhy
Azure Key Vaultuser_impersonationRead secrets and certificates from your Key Vaults.
Azure Service Managementuser_impersonationManage ARM resources (clusters, networking, resource groups).
Microsoft GraphemailRead the signed-in user's email for identity display.
Microsoft GraphopenidStandard OpenID Connect authentication.
Microsoft GraphprofileRead user profile for identity display.
Microsoft GraphUser.ReadRead the signed-in user's basic profile.

Azure RBAC Role Assignments

These are the Azure resource roles assigned to the "Codiac Relay" principal:

Resource ScopeRoleWhy
Each SubscriptionAzure Kubernetes Service Cluster AdminCreate, manage, and access AKS clusters. Includes listClusterAdminCredential for kubeconfig retrieval.
Each ACRAcrPullPull container images during deployments.
Each ACRAcrPushPush container images during builds.
Each ACRAcrDeleteClean up old images.
Each ACRReaderList and read ACR metadata.
Each Key VaultKey Vault ReaderRead secrets. Codiac does not write to Key Vault.
tip

The PDF guide references Key Vault Contributor, but Codiac currently only reads from Key Vault. Key Vault Reader is sufficient and follows least privilege. Use Key Vault Contributor only if you plan to use future Codiac features that write to Key Vault.

ARM Permissions (What the RBAC Roles Grant)

For full transparency, here are the specific ARM actions the roles above translate to during cluster creation:

OperationARM ActionPerformed By
Create/check Resource GroupMicrosoft.Resources/subscriptions/resourceGroups/read, writeCodiac Relay
Create AKS ClusterMicrosoft.ContainerService/managedClusters/write, readCodiac Relay
Get Cluster CredentialsMicrosoft.ContainerService/managedClusters/listClusterAdminCredential/actionCodiac Relay
Create Public IP (for ingress)Microsoft.Network/publicIPAddresses/write, readCodiac Relay
Assign ACRPull to cluster SPMicrosoft.Authorization/roleAssignments/writeCodiac Relay
Pull images from ACRMicrosoft.ContainerRegistry/registries/pullPer-Cluster SP

MS Graph Permissions (For Per-Cluster SP Creation)

When Codiac auto-creates a per-cluster Service Principal, it makes these MS Graph API calls using the logged-in user's delegated permissions:

Graph EndpointActionPermission Required
POST /applicationsCreate App Registration for the clusterApplication.ReadWrite.All
POST /servicePrincipalsCreate the Service Principal from the App RegistrationApplication.ReadWrite.All
POST /servicePrincipals/{id}/addPasswordGenerate credentials for the cluster SPApplication.ReadWrite.All
GET /servicePrincipals?$filter=...Check if SP already existsApplication.Read.All
Bring Your Own Service Principal

If your organization cannot grant Cloud Application Administrator to the Codiac setup user, you can pre-create the cluster Service Principal yourself and provide it to Codiac during cluster creation. This removes the need for the elevated Entra role. (Support for this workflow is coming soon.)


Setup Instructions

Prerequisites

Before you begin, ensure:

  • You have access to the Azure Portal.
  • Your user account has the Cloud Application Administrator and Privileged Role Administrator Entra roles (or a Global Administrator can perform the setup).
  • You know which Azure subscriptions, ACRs, and Key Vaults Codiac should have access to.

Option A: Manual Setup (Portal)

Step 1: Create the App Registration

  1. Log into your Azure Portal.
  2. Navigate to Microsoft Entra ID (formerly Azure AD).
  3. Click App Registrations > New registration.
  4. Set the name to Codiac Relay.
  5. Set Supported account types to Accounts in this organizational directory only (Single Tenant).
  6. Click Register.
  7. From the App Registration overview, click Authentication in the left column.
    • Click Add a platform > Mobile and desktop applications.
    • Add this Custom Redirect URI: http://localhost:5799/csp/azure/login/auth-code-catcher
    • Click Configure.
  8. Still on the Authentication page, enable Allow public client flows and click Save.

Step 2: Add API Permissions

  1. Click API Permissions in the left column.
  2. Click Add a permission > APIs my organization uses.
  3. Add the following permissions:
    • Azure Key Vault: user_impersonation
    • Azure Service Management: user_impersonation
    • Microsoft Graph: email, openid, profile, User.Read
  4. Click Grant admin consent for your directory.

Step 3: Create a Client Secret

  1. Click Certificates & secrets in the left column.
  2. Click New client secret.
  3. Store the generated secret value in a Key Vault for safekeeping.
info

The client secret is used for calling the Codiac CLI from CI/CD pipelines using the client-credentials auth flow. It is not required for interactive usage.

Step 4: Assign Entra Directory Role

  1. Navigate to Microsoft Entra ID > Roles and administrators > All roles.
  2. Search for and select Cloud Application Administrator.
  3. Click Assignments > Active assignments > Add assignments.
  4. Select the user(s) who will create clusters through Codiac and confirm.

Step 5: Assign Subscription Roles

For each Azure subscription that Codiac will manage:

  1. Open the subscription > Access control (IAM).
  2. Click Add > Add role assignment.
  3. Assign the Azure Kubernetes Service Cluster Admin role to the Codiac Relay principal.

Step 6: Assign ACR Roles

For each Azure Container Registry that Codiac will use:

  1. Open the ACR > Access control (IAM).
  2. Assign these roles to the Codiac Relay principal:
    • Reader
    • AcrPull
    • AcrPush
    • AcrDelete

Step 7: Assign Key Vault Roles

For each Azure Key Vault that Codiac will read from:

  1. Open the Key Vault > Access control (IAM).
  2. Assign the Key Vault Reader role to the Codiac Relay principal.

Option B: Azure CLI Script

The script below automates the full setup. Copy it, fill in the variables at the top, and run it in a terminal with the Azure CLI installed.

warning

You must be logged in as a user with Cloud Application Administrator and Privileged Role Administrator Entra roles before running this script.

#!/usr/bin/env bash
set -euo pipefail

# ============================================================================
# CODIAC RELAY — Azure CSP Tenant Profile Setup Script
# ============================================================================
# Fill in these variables before running:
# ============================================================================

# Your Azure Tenant ID (find in Entra ID > Overview)
TENANT_ID="<YOUR-TENANT-ID>"

# Comma-separated subscription IDs that Codiac should manage
SUBSCRIPTION_IDS="<SUBSCRIPTION-ID-1>,<SUBSCRIPTION-ID-2>"

# Comma-separated ACR names (just the name, not the full URL)
ACR_NAMES="<ACR-NAME-1>,<ACR-NAME-2>"

# Comma-separated Key Vault names
KEYVAULT_NAMES="<KEYVAULT-NAME-1>"

# Display name for the App Registration
APP_NAME="Codiac Relay"

# ============================================================================
# DO NOT EDIT BELOW THIS LINE
# ============================================================================

echo "============================================"
echo " Codiac Relay — Azure CSP Setup"
echo "============================================"
echo ""

# ---- Login ----
echo "Ensuring Azure CLI login..."
az account show > /dev/null 2>&1 || az login --tenant "$TENANT_ID"
echo ""

# ---- Step 1: Create App Registration ----
echo "[Step 1/6] Creating App Registration: $APP_NAME ..."
APP_ID=$(az ad app list --display-name "$APP_NAME" --query "[0].appId" -o tsv 2>/dev/null)

if [ -z "$APP_ID" ] || [ "$APP_ID" = "None" ]; then
APP_ID=$(az ad app create \
--display-name "$APP_NAME" \
--sign-in-audience "AzureADMyOrg" \
--enable-access-token-issuance false \
--enable-id-token-issuance false \
--is-fallback-public-client true \
--public-client-redirect-uris "http://localhost:5799/csp/azure/login/auth-code-catcher" \
--query "appId" -o tsv)
echo " Created App Registration with appId: $APP_ID"
else
echo " Found existing App Registration with appId: $APP_ID"
fi

# ---- Step 2: Create Service Principal (Enterprise Application) ----
echo "[Step 2/6] Creating Service Principal..."
SP_OBJECT_ID=$(az ad sp list --filter "appId eq '$APP_ID'" --query "[0].id" -o tsv 2>/dev/null)

if [ -z "$SP_OBJECT_ID" ] || [ "$SP_OBJECT_ID" = "None" ]; then
SP_OBJECT_ID=$(az ad sp create --id "$APP_ID" --query "id" -o tsv)
echo " Created Service Principal with objectId: $SP_OBJECT_ID"
else
echo " Found existing Service Principal with objectId: $SP_OBJECT_ID"
fi

# ---- Step 3: Add API Permissions ----
echo "[Step 3/6] Adding API permissions..."

# Azure Key Vault — user_impersonation
KV_API_ID="cfa8b339-82a2-471a-a3c9-0fc0be7a4093"
KV_SCOPE_ID=$(az ad sp show --id "$KV_API_ID" --query "oauth2PermissionScopes[?value=='user_impersonation'].id" -o tsv 2>/dev/null)
if [ -n "$KV_SCOPE_ID" ]; then
az ad app permission add --id "$APP_ID" --api "$KV_API_ID" --api-permissions "${KV_SCOPE_ID}=Scope" 2>/dev/null || true
echo " Added: Azure Key Vault — user_impersonation"
fi

# Azure Service Management — user_impersonation
ASM_API_ID="797f4846-ba00-4fd7-ba43-dac1f8f63013"
ASM_SCOPE_ID=$(az ad sp show --id "$ASM_API_ID" --query "oauth2PermissionScopes[?value=='user_impersonation'].id" -o tsv 2>/dev/null)
if [ -n "$ASM_SCOPE_ID" ]; then
az ad app permission add --id "$APP_ID" --api "$ASM_API_ID" --api-permissions "${ASM_SCOPE_ID}=Scope" 2>/dev/null || true
echo " Added: Azure Service Management — user_impersonation"
fi

# Microsoft Graph — email, openid, profile, User.Read
GRAPH_API_ID="00000003-0000-0000-c000-000000000000"
GRAPH_EMAIL_ID="64a6cdd6-aab1-4aaf-94b8-3cc8405e90d0"
GRAPH_OPENID_ID="37f7f235-527c-4136-accd-4a02d197296e"
GRAPH_PROFILE_ID="14dad69e-099b-42c9-810b-d002981feec1"
GRAPH_USERREAD_ID="e1fe6dd8-ba31-4d61-89e7-88639da4683d"
az ad app permission add --id "$APP_ID" --api "$GRAPH_API_ID" \
--api-permissions "${GRAPH_EMAIL_ID}=Scope ${GRAPH_OPENID_ID}=Scope ${GRAPH_PROFILE_ID}=Scope ${GRAPH_USERREAD_ID}=Scope" 2>/dev/null || true
echo " Added: Microsoft Graph — email, openid, profile, User.Read"

# Grant admin consent
echo " Granting admin consent..."
az ad app permission admin-consent --id "$APP_ID" 2>/dev/null || echo " (Admin consent may require manual approval in the portal)"

# ---- Step 4: Create Client Secret ----
echo "[Step 4/6] Creating client secret..."
SECRET_VALUE=$(az ad app credential reset --id "$APP_ID" --display-name "CodiacPrimary" --years 2 --query "password" -o tsv 2>/dev/null)
if [ -n "$SECRET_VALUE" ]; then
echo " Client secret created. Store this securely — it will not be shown again:"
echo ""
echo " $SECRET_VALUE"
echo ""
else
echo " Could not create client secret. Create one manually in the portal."
fi

# ---- Step 5: Assign Subscription Roles ----
echo "[Step 5/6] Assigning subscription roles..."
IFS=',' read -ra SUBS <<< "$SUBSCRIPTION_IDS"
for SUB_ID in "${SUBS[@]}"; do
SUB_ID=$(echo "$SUB_ID" | xargs) # trim whitespace
echo " Subscription: $SUB_ID"
az role assignment create \
--assignee "$SP_OBJECT_ID" \
--role "Azure Kubernetes Service Cluster Admin Role" \
--scope "/subscriptions/$SUB_ID" \
--only-show-errors 2>/dev/null || echo " (Role may already be assigned)"
echo " Assigned: Azure Kubernetes Service Cluster Admin"
done

# ---- Step 6: Assign ACR Roles ----
echo "[Step 6/6] Assigning ACR and Key Vault roles..."
IFS=',' read -ra ACRS <<< "$ACR_NAMES"
for ACR_NAME in "${ACRS[@]}"; do
ACR_NAME=$(echo "$ACR_NAME" | xargs)
ACR_ID=$(az acr show --name "$ACR_NAME" --query "id" -o tsv 2>/dev/null)
if [ -n "$ACR_ID" ]; then
echo " ACR: $ACR_NAME"
for ROLE in "Reader" "AcrPull" "AcrPush" "AcrDelete"; do
az role assignment create \
--assignee "$SP_OBJECT_ID" \
--role "$ROLE" \
--scope "$ACR_ID" \
--only-show-errors 2>/dev/null || true
echo " Assigned: $ROLE"
done
else
echo " ACR not found: $ACR_NAME (skipping)"
fi
done

IFS=',' read -ra KVS <<< "$KEYVAULT_NAMES"
for KV_NAME in "${KVS[@]}"; do
KV_NAME=$(echo "$KV_NAME" | xargs)
KV_ID=$(az keyvault show --name "$KV_NAME" --query "id" -o tsv 2>/dev/null)
if [ -n "$KV_ID" ]; then
echo " Key Vault: $KV_NAME"
az role assignment create \
--assignee "$SP_OBJECT_ID" \
--role "Key Vault Reader" \
--scope "$KV_ID" \
--only-show-errors 2>/dev/null || true
echo " Assigned: Key Vault Reader"
else
echo " Key Vault not found: $KV_NAME (skipping)"
fi
done

# ---- Summary ----
echo ""
echo "============================================"
echo " Setup Complete"
echo "============================================"
echo ""
echo " App Registration: $APP_NAME"
echo " Application ID: $APP_ID"
echo " SP Object ID: $SP_OBJECT_ID"
echo " Tenant ID: $TENANT_ID"
echo ""
echo " Use these values when configuring the CSP"
echo " Tenant Profile in Codiac."
echo ""
echo "============================================"

Option C: Verify Existing Permissions

Already set up Codiac but running into permission errors during cluster creation? Run this script to check what's missing:

#!/usr/bin/env bash
set -euo pipefail

# ============================================================================
# CODIAC RELAY — Azure Permission Verification Script
# ============================================================================
# Fill in these variables:
# ============================================================================

APP_NAME="Codiac Relay"
SUBSCRIPTION_ID="<YOUR-SUBSCRIPTION-ID>"

# Optional — leave empty to skip ACR/KV checks
ACR_NAME=""
KEYVAULT_NAME=""

# ============================================================================
# DO NOT EDIT BELOW THIS LINE
# ============================================================================

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

pass() { echo -e " ${GREEN}PASS${NC} $1"; }
fail() { echo -e " ${RED}FAIL${NC} $1"; }
warn() { echo -e " ${YELLOW}WARN${NC} $1"; }

echo ""
echo "============================================"
echo " Codiac Relay — Permission Check"
echo "============================================"
echo ""

# ---- Check Azure CLI login ----
echo "Checking Azure CLI login..."
CURRENT_USER=$(az account show --query "user.name" -o tsv 2>/dev/null) || {
fail "Not logged in. Run: az login"
exit 1
}
pass "Logged in as: $CURRENT_USER"
echo ""

# ---- Check App Registration exists ----
echo "Checking App Registration..."
APP_ID=$(az ad app list --display-name "$APP_NAME" --query "[0].appId" -o tsv 2>/dev/null)
if [ -z "$APP_ID" ] || [ "$APP_ID" = "None" ]; then
fail "App Registration '$APP_NAME' not found."
echo " Run the setup script or create it manually."
exit 1
fi
pass "App Registration found: $APP_ID"

# ---- Check Service Principal exists ----
SP_OBJECT_ID=$(az ad sp list --filter "appId eq '$APP_ID'" --query "[0].id" -o tsv 2>/dev/null)
if [ -z "$SP_OBJECT_ID" ] || [ "$SP_OBJECT_ID" = "None" ]; then
fail "Service Principal not found for App Registration."
else
pass "Service Principal found: $SP_OBJECT_ID"
fi

# ---- Check public client flows ----
IS_PUBLIC=$(az ad app show --id "$APP_ID" --query "isFallbackPublicClient" -o tsv 2>/dev/null)
if [ "$IS_PUBLIC" = "true" ]; then
pass "Public client flows enabled"
else
fail "Public client flows not enabled. Enable in Authentication settings."
fi

# ---- Check redirect URI ----
REDIRECT_URI="http://localhost:5799/csp/azure/login/auth-code-catcher"
HAS_REDIRECT=$(az ad app show --id "$APP_ID" --query "publicClient.redirectUris[?@ == '$REDIRECT_URI']" -o tsv 2>/dev/null)
if [ -n "$HAS_REDIRECT" ]; then
pass "Redirect URI configured"
else
fail "Redirect URI missing: $REDIRECT_URI"
fi
echo ""

# ---- Check API Permissions ----
echo "Checking API permissions..."
PERMS=$(az ad app permission list --id "$APP_ID" -o json 2>/dev/null)

check_api_perm() {
local api_id="$1"
local api_name="$2"
local found
found=$(echo "$PERMS" | python3 -c "
import sys, json
perms = json.load(sys.stdin)
found = any(p.get('resourceAppId') == '$api_id' for p in perms)
print('true' if found else 'false')
" 2>/dev/null || echo "false")
if [ "$found" = "true" ]; then
pass "$api_name"
else
fail "$api_name — not configured"
fi
}

check_api_perm "cfa8b339-82a2-471a-a3c9-0fc0be7a4093" "Azure Key Vault"
check_api_perm "797f4846-ba00-4fd7-ba43-dac1f8f63013" "Azure Service Management"
check_api_perm "00000003-0000-0000-c000-000000000000" "Microsoft Graph"
echo ""

# ---- Check Subscription Role Assignments ----
echo "Checking subscription role assignments..."
if [ -n "$SUBSCRIPTION_ID" ] && [ "$SUBSCRIPTION_ID" != "<YOUR-SUBSCRIPTION-ID>" ]; then
ROLE_CHECK=$(az role assignment list \
--assignee "$SP_OBJECT_ID" \
--scope "/subscriptions/$SUBSCRIPTION_ID" \
--query "[?roleDefinitionName=='Azure Kubernetes Service Cluster Admin Role'].roleDefinitionName" \
-o tsv 2>/dev/null)
if [ -n "$ROLE_CHECK" ]; then
pass "Azure Kubernetes Service Cluster Admin on subscription $SUBSCRIPTION_ID"
else
fail "Missing: Azure Kubernetes Service Cluster Admin on subscription $SUBSCRIPTION_ID"
fi
else
warn "Subscription ID not provided — skipping subscription role check."
fi
echo ""

# ---- Check ACR Role Assignments ----
if [ -n "$ACR_NAME" ]; then
echo "Checking ACR role assignments for: $ACR_NAME ..."
ACR_ID=$(az acr show --name "$ACR_NAME" --query "id" -o tsv 2>/dev/null)
if [ -n "$ACR_ID" ]; then
for ROLE in "Reader" "AcrPull" "AcrPush" "AcrDelete"; do
ROLE_CHECK=$(az role assignment list \
--assignee "$SP_OBJECT_ID" \
--scope "$ACR_ID" \
--query "[?roleDefinitionName=='$ROLE'].roleDefinitionName" \
-o tsv 2>/dev/null)
if [ -n "$ROLE_CHECK" ]; then
pass "$ROLE on $ACR_NAME"
else
fail "Missing: $ROLE on $ACR_NAME"
fi
done
else
fail "ACR not found: $ACR_NAME"
fi
echo ""
fi

# ---- Check Key Vault Role Assignments ----
if [ -n "$KEYVAULT_NAME" ]; then
echo "Checking Key Vault role assignments for: $KEYVAULT_NAME ..."
KV_ID=$(az keyvault show --name "$KEYVAULT_NAME" --query "id" -o tsv 2>/dev/null)
if [ -n "$KV_ID" ]; then
ROLE_CHECK=$(az role assignment list \
--assignee "$SP_OBJECT_ID" \
--scope "$KV_ID" \
--query "[?roleDefinitionName=='Key Vault Reader' || roleDefinitionName=='Key Vault Contributor'].roleDefinitionName" \
-o tsv 2>/dev/null)
if [ -n "$ROLE_CHECK" ]; then
pass "$ROLE_CHECK on $KEYVAULT_NAME"
else
fail "Missing: Key Vault Reader (or Contributor) on $KEYVAULT_NAME"
fi
else
fail "Key Vault not found: $KEYVAULT_NAME"
fi
echo ""
fi

# ---- Check current user's Entra roles ----
echo "Checking your Entra ID roles (for cluster creation)..."
USER_OBJECT_ID=$(az ad signed-in-user show --query "id" -o tsv 2>/dev/null)
if [ -n "$USER_OBJECT_ID" ]; then
USER_ROLES=$(az rest --method GET \
--url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments?\$filter=principalId eq '$USER_OBJECT_ID'" \
--query "value[].roleDefinitionId" -o tsv 2>/dev/null)

# Cloud Application Administrator role definition ID
CLOUD_APP_ADMIN_ID="158c047a-c907-4556-b7ef-446551a6b5f7"
if echo "$USER_ROLES" | grep -q "$CLOUD_APP_ADMIN_ID"; then
pass "You have Cloud Application Administrator"
else
warn "You may not have Cloud Application Administrator. This is needed to create per-cluster Service Principals."
echo " If cluster creation fails, ask a Global Admin to assign this role to your account."
fi
else
warn "Could not determine your Entra roles. Ensure you have Cloud Application Administrator for cluster creation."
fi

echo ""
echo "============================================"
echo " Verification Complete"
echo "============================================"
echo ""

Troubleshooting

"Insufficient privileges to complete the operation"

The logged-in user needs the Cloud Application Administrator Entra role to create per-cluster Service Principals. Ask your Azure administrator to assign this role.

"Authorization_RequestDenied" on MS Graph calls

The Codiac App Registration is missing API permissions or admin consent has not been granted. Re-run Step 2 of the setup and ensure you click Grant admin consent.

"RoleAssignmentExists" during setup

This is safe to ignore — it means the role assignment already exists.

"The client does not have authorization to perform action"

The Codiac Relay principal is missing an Azure RBAC role on the target resource. Run the verification script to identify which role is missing.

Further Reading