Skip to main content
Version: Next

Task 01: Bootstrap

Runbook Azure

DOCUMENT CATEGORY: Runbook Step SCOPE: CI/CD service principal bootstrap PURPOSE: Create an Entra ID app registration and service principal for CI/CD pipeline authentication

Status: Active Applies To: All Azure Local deployments Last Updated: 2026-03-19


Objective

Create an Entra ID app registration and service principal in the target tenant so CI/CD pipelines can authenticate to Azure and manage resources.


Prerequisites

  • Azure CLI installed and authenticated (az login)
  • Signed in to the target tenant (verify with az account show)
  • Application Administrator or Global Administrator role in Entra ID
  • Owner or User Access Administrator on each target subscription
  • Target subscription ID(s) available

Variables from variables.yml

VariableConfig PathExample
Tenant IDazure.tenant_idxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Target Subscription IDazure.subscription.idxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Procedure

Step 1: Create the App Registration and Service Principal

# Set variables
APP_NAME="azurelocal-cicd"
SUBSCRIPTION_IDS=("subscription-id-1" "subscription-id-2")

# Create app registration with a 2-year secret
az ad app create --display-name "$APP_NAME"
APP_ID=$(az ad app list --display-name "$APP_NAME" --query "[0].appId" -o tsv)

# Create service principal
az ad sp create --id "$APP_ID"

# Create client secret (2-year expiry)
SECRET=$(az ad app credential reset --id "$APP_ID" --years 2 --query "password" -o tsv)

# Get tenant ID
TENANT_ID=$(az account show --query "tenantId" -o tsv)

echo "ARM_CLIENT_ID: $APP_ID"
echo "ARM_CLIENT_SECRET: $SECRET"
echo "ARM_TENANT_ID: $TENANT_ID"

Important: Save the ARM_CLIENT_SECRET immediately — it cannot be retrieved again.

Step 2: Assign RBAC Roles

Assign roles on each target subscription:

for SUB_ID in "${SUBSCRIPTION_IDS[@]}"; do
# Contributor — manage resources
az role assignment create \
--assignee "$APP_ID" \
--role "Contributor" \
--scope "/subscriptions/$SUB_ID"

# User Access Administrator — manage RBAC assignments
az role assignment create \
--assignee "$APP_ID" \
--role "User Access Administrator" \
--scope "/subscriptions/$SUB_ID"
done

Step 3: Register Providers and Features

for SUB_ID in "${SUBSCRIPTION_IDS[@]}"; do
az account set --subscription "$SUB_ID"

# Register the Compute provider
az provider register --namespace "Microsoft.Compute"

# Enable EncryptionAtHost feature
az feature register \
--namespace "Microsoft.Compute" \
--name "EncryptionAtHost"
done

Step 4: Add API Permissions

# Add Microsoft Graph — RoleManagement.ReadWrite.Directory (Application)
GRAPH_APP_ID="00000003-0000-0000-c000-000000000000"
ROLE_MGMT_ID="9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8"

az ad app permission add \
--id "$APP_ID" \
--api "$GRAPH_APP_ID" \
--api-permissions "$ROLE_MGMT_ID=Role"

Grant admin consent via the Azure Portal or CLI:

  1. Navigate to Entra IDApp registrationsazurelocal-cicd
  2. Select API permissions
  3. Click Grant admin consent for <tenant>

Verification

  • App registration azurelocal-cicd exists in Entra ID
  • Service principal created
  • Contributor and User Access Administrator roles assigned on all target subscriptions
  • Microsoft.Compute provider registered
  • EncryptionAtHost feature enabled
  • Admin consent granted for API permissions
  • Credentials saved securely (will be stored in CI/CD platform in Task 05)

Troubleshooting

App already exists:

An app registration with the name "azurelocal-cicd" already exists.

→ Delete the existing app registration first: az ad app delete --id <APP_ID>

Insufficient permissions:

Insufficient privileges to complete the operation.

→ Ensure you have Application Administrator role in Entra ID and Owner on the target subscription(s)

Feature registration pending:

Feature "EncryptionAtHost" is in "Registering" state.

→ Wait a few minutes, then check status: az feature show --namespace Microsoft.Compute --name EncryptionAtHost


References


Next Steps

Create Source Control Project