These are instructions for installing a Rack via the command line. The easiest way to install a Rack is with the Convox Web Console
Tool | Docs |
---|---|
Azure CLI | https://docs.microsoft.com/cli/azure/install-azure-cli |
Terraform (optional) | https://developer.hashicorp.com/terraform/tutorials |
Convox CLI | /installation/cli |
# sign‑in to Azure
az login
Expected output:
A web browser has been opened at https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize.
Please continue the login in the web browser.
[Tenant and subscription selection]
No Subscription name Subscription ID Tenant
----- ------------------- ------------------------------------ --------
[1] * Your Subscription xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx YourTenant
The default is marked with an *; the default tenant is 'YourTenant' and subscription is 'Your Subscription'.
Variable | Where to get it |
---|---|
ARM_CLIENT_ID |
appId of the service‑principal you create below |
ARM_CLIENT_SECRET |
password returned when the SP is created |
ARM_SUBSCRIPTION_ID |
az account show --query id -o tsv |
ARM_TENANT_ID |
az account show --query tenantId -o tsv |
# List available subscriptions
az account list --output table
# Set your subscription (replace with your actual subscription ID)
az account set --subscription <SUBSCRIPTION_ID>
# Export the subscription ID for later use
export ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv)
# Verify the subscription ID is set
echo "Subscription ID: $ARM_SUBSCRIPTION_ID"
Expected output:
# az account list --output table
Name CloudName SubscriptionId State IsDefault
--------------- ----------- ------------------------------------ ------- -----------
Your Subscription AzureCloud xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Enabled True
# echo "Subscription ID: $ARM_SUBSCRIPTION_ID"
Subscription ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
az ad sp create-for-rbac \
--name terraform \
--role Owner \
--scopes "/subscriptions/$ARM_SUBSCRIPTION_ID"
Expected output:
{
"appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"displayName": "terraform",
"password": "xxxxx~xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
Set environment variables from the output:
# Copy the appId from the output above
export ARM_CLIENT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# Copy the password from the output above
export ARM_CLIENT_SECRET="xxxxx~xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Copy the tenant from the output above
export ARM_TENANT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Note: The original documentation had an incorrect permission ID. Use the corrected commands below:
# Add User.Read delegated permission (corrected permission ID)
az ad app permission add --id $ARM_CLIENT_ID \
--api 00000003-0000-0000-c000-000000000000 \
--api-permissions e1fe6dd8-ba31-4d61-89e7-88639da4683d=Scope
# Grant the permission
az ad app permission grant --id $ARM_CLIENT_ID \
--api 00000003-0000-0000-c000-000000000000 \
--consent-type AllPrincipals --scope User.Read
# Provide admin consent
az ad app permission admin-consent --id $ARM_CLIENT_ID
Expected output:
# First command output:
Invoking `az ad app permission grant --id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --api 00000003-0000-0000-c000-000000000000` is needed to make the change effective
# Second command output:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#oauth2PermissionGrants/$entity",
"clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"consentType": "AllPrincipals",
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"principalId": null,
"resourceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"scope": "User.Read"
}
# Third command completes silently if successful
Terraform/Convox must be able to create Azure AD (Entra) application objects. Because Azure RBAC roles do not flow into Entra ID, you need to grant the service‑principal a directory role.
The quickest safe choice is Application Administrator. (Cloud Application Administrator also works.)
# 1) Get the internal roleDefinitionId for "Application Administrator"
ROLE_ID=$(az rest --method GET \
--url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions?\$filter=displayName eq 'Application Administrator'" \
--query 'value[0].id' -o tsv)
# Verify the role ID was retrieved
echo "Role ID: $ROLE_ID"
# 2) Get the objectId of the SP you just created
SP_OBJECT_ID=$(az ad sp list --display-name "terraform" --query "[0].id" -o tsv)
# Verify the service principal object ID was retrieved
echo "Service Principal Object ID: $SP_OBJECT_ID"
# 3) Assign the role at tenant scope ("/") - CORRECTED with Content-Type header
az rest --method POST \
--url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments" \
--headers "Content-Type=application/json" \
--body "{\"principalId\": \"${SP_OBJECT_ID}\", \"roleDefinitionId\": \"${ROLE_ID}\", \"directoryScopeId\": \"/\"}"
Expected output:
# echo "Role ID: $ROLE_ID"
Role ID: 9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3
# echo "Service Principal Object ID: $SP_OBJECT_ID"
Service Principal Object ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# Role assignment command output:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#roleManagement/directory/roleAssignments/$entity",
"directoryScopeId": "/",
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"principalId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"roleDefinitionId": "9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3"
}
Before proceeding to install the Rack, verify all required environment variables are set:
echo "ARM_SUBSCRIPTION_ID: $ARM_SUBSCRIPTION_ID"
echo "ARM_CLIENT_ID: $ARM_CLIENT_ID"
echo "ARM_CLIENT_SECRET: $ARM_CLIENT_SECRET"
echo "ARM_TENANT_ID: $ARM_TENANT_ID"
Expected output:
ARM_SUBSCRIPTION_ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ARM_CLIENT_ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ARM_CLIENT_SECRET: xxxxx~xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ARM_TENANT_ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Important: If any of these are empty, go back and re-run the relevant commands from steps 3-4.
convox rack install azure <name> [param=value]…
Parameter | Default | Description |
---|---|---|
cert_duration |
2160h |
How often certificates renew |
node_type |
Standard_D3_v3 |
VM size for Kubernetes nodes |
region |
eastus |
Azure region |
syslog |
Forward logs to a syslog endpoint |
Example:
convox rack install azure my-rack region=westus2 node_type=Standard_D2_v3
Cause: Using incorrect permission ID in step 5.
Solution: Ensure you’re using the corrected permission ID: e1fe6dd8-ba31-4d61-89e7-88639da4683d
Cause: Missing Content-Type header in the role assignment REST call.
Solution: Ensure you include --headers "Content-Type=application/json"
in the az rest command.
Cause: Application Administrator role assignment failed or incomplete. Solution:
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments?\$filter=principalId eq '${SP_OBJECT_ID}'"
Cause: Variables only exist in current shell session. Solution: Either re-export them or add them to your shell profile:
# Add to ~/.bashrc or ~/.zshrc
export ARM_SUBSCRIPTION_ID="your-subscription-id"
export ARM_CLIENT_ID="your-client-id"
export ARM_CLIENT_SECRET="your-client-secret"
export ARM_TENANT_ID="your-tenant-id"
To verify your setup is correct before installing:
# Test Azure CLI authentication
az account show
# Test service principal can access subscription
az login --service-principal -u $ARM_CLIENT_ID -p $ARM_CLIENT_SECRET --tenant $ARM_TENANT_ID
az account show
# Switch back to your user account
az login
Instead of installing the Rack via CLI, you can use the Convox Console to create and manage your Azure integration. This provides a web-based interface for Rack management.
You’ll need the following values from the previous steps. Here’s where to find each one:
Console Field | Source | Example Value |
---|---|---|
Subscription ID | From az login output or $ARM_SUBSCRIPTION_ID |
12345678-1234-1234-1234-123456789abc |
Tenant ID | From az login output or service principal tenant field |
87654321-4321-4321-4321-cba987654321 |
Client ID | Service principal appId field |
abcdef12-3456-7890-abcd-ef1234567890 |
Client Secret | Service principal password field |
ABC8Q~X12DeF34gH56iJ78kL90mN-OpQr23StUv |
From your service principal creation output:
{
"appId": "abcdef12-3456-7890-abcd-ef1234567890", ← Client ID
"displayName": "terraform",
"password": "ABC8Q~X12DeF34gH56iJ78kL90mN-OpQr23StUv", ← Client Secret
"tenant": "87654321-4321-4321-4321-cba987654321" ← Tenant ID
}
Quick reference commands to get these values:
# Subscription ID
echo "Subscription ID: $ARM_SUBSCRIPTION_ID"
# Or get it directly
az account show --query id -o tsv
# Tenant ID
az account show --query tenantId -o tsv
# Client ID and Secret (from your service principal output above)
echo "Client ID: $ARM_CLIENT_ID"
echo "Client Secret: $ARM_CLIENT_SECRET"
Note: You still need to complete steps 1-6 from this documentation to create the properly configured service principal, regardless of whether you use CLI or Console installation.