Connect Azure Subscription
Connect your Azure subscription using federated credentials for secure, secret-free authentication.
Prerequisites
- Azure subscription with active billing
OwnerorUser Access Administratorrole on the subscriptionAzure CLIinstalled and configured
Option 1: Federated Credentials (Recommended)
Federated credentials allow NightOps to authenticate without storing client secrets.
Step 1: Create App Registration
# Create the app registration
az ad app create \
--display-name "NightOps" \
--sign-in-audience AzureADMyOrg
# Get the app ID
APP_ID=$(az ad app list --display-name "NightOps" --query "[0].appId" -o tsv)
echo "App ID: $APP_ID"
Step 2: Create Service Principal
az ad sp create --id $APP_ID
# Get the service principal object ID
SP_OBJECT_ID=$(az ad sp show --id $APP_ID --query "id" -o tsv)
echo "Service Principal Object ID: $SP_OBJECT_ID"
Step 3: Add Federated Credentials
# Get your NightOps org ID from the dashboard
NIGHTOPS_ORG_ID="your-org-id-from-dashboard"
# Create federated credential
az ad app federated-credential create \
--id $APP_ID \
--parameters '{
"name": "nightops-federation",
"issuer": "https://auth.nightops.io",
"subject": "org:'$NIGHTOPS_ORG_ID'",
"audiences": ["api://AzureADTokenExchange"]
}'
Step 4: Assign RBAC Role
SUBSCRIPTION_ID=$(az account show --query "id" -o tsv)
# Create custom role for NightOps
az role definition create --role-definition '{
"Name": "NightOps Resource Manager",
"Description": "Allows NightOps to manage compute resources for cost optimization",
"Actions": [
"Microsoft.Compute/virtualMachines/read",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/deallocate/action",
"Microsoft.Compute/virtualMachines/powerOff/action",
"Microsoft.Compute/virtualMachineScaleSets/read",
"Microsoft.Compute/virtualMachineScaleSets/write",
"Microsoft.Sql/servers/databases/read",
"Microsoft.Sql/servers/databases/pause/action",
"Microsoft.Sql/servers/databases/resume/action",
"Microsoft.ContainerService/managedClusters/read",
"Microsoft.ContainerService/managedClusters/agentPools/read",
"Microsoft.ContainerService/managedClusters/agentPools/write",
"Microsoft.Resources/subscriptions/resourceGroups/read"
],
"NotActions": [],
"AssignableScopes": ["/subscriptions/'$SUBSCRIPTION_ID'"]
}'
# Assign the role to the service principal
az role assignment create \
--assignee $SP_OBJECT_ID \
--role "NightOps Resource Manager" \
--scope "/subscriptions/$SUBSCRIPTION_ID"
Step 5: Get Configuration Values
# Get tenant ID
TENANT_ID=$(az account show --query "tenantId" -o tsv)
echo "Tenant ID: $TENANT_ID"
echo "Subscription ID: $SUBSCRIPTION_ID"
echo "Client ID: $APP_ID"
Step 6: Configure in NightOps
- Go to Providers → Add Provider → Azure
- Enter your Tenant ID
- Enter your Subscription ID
- Enter your Client ID (App ID)
- Select which Regions to scan
- Click Test Connection
- Click Save Provider
Option 2: Client Secret (Not Recommended)
For environments that don't support federated credentials:
Create Client Secret
# Create secret (valid for 1 year)
az ad app credential reset \
--id $APP_ID \
--years 1
# Output includes the client secret - save it securely
Configure in NightOps
- Go to Providers → Add Provider → Azure
- Select Client Secret
- Enter the client secret
- Click Save Provider
Client secrets are less secure than federated credentials. Secrets can be leaked, require rotation, and are harder to audit. Use federated credentials when possible.
Multi-Region Support
NightOps can manage resources across multiple Azure regions.
When adding the provider, select which regions to scan:
Regions: eastus, westus2, westeurope
Multi-Subscription Setup
For organizations with multiple Azure subscriptions:
Option A: Separate Providers
Create an app registration and role assignment in each subscription. Add each as a separate provider in NightOps.
Option B: Management Group Access
Assign the role at the management group level to cover multiple subscriptions:
MANAGEMENT_GROUP_ID="your-management-group-id"
az role assignment create \
--assignee $SP_OBJECT_ID \
--role "NightOps Resource Manager" \
--scope "/providers/Microsoft.Management/managementGroups/$MANAGEMENT_GROUP_ID"
Terraform Module
# nightops.tf
variable "subscription_id" {
type = string
}
variable "nightops_org_id" {
description = "NightOps organization ID from dashboard"
type = string
}
data "azurerm_subscription" "current" {}
data "azuread_client_config" "current" {}
# App Registration
resource "azuread_application" "nightops" {
display_name = "NightOps"
owners = [data.azuread_client_config.current.object_id]
}
# Service Principal
resource "azuread_service_principal" "nightops" {
client_id = azuread_application.nightops.client_id
owners = [data.azuread_client_config.current.object_id]
}
# Federated Credential
resource "azuread_application_federated_identity_credential" "nightops" {
application_id = azuread_application.nightops.id
display_name = "nightops-federation"
issuer = "https://auth.nightops.io"
subject = "org:${var.nightops_org_id}"
audiences = ["api://AzureADTokenExchange"]
}
# Custom Role Definition
resource "azurerm_role_definition" "nightops" {
name = "NightOps Resource Manager"
scope = data.azurerm_subscription.current.id
description = "Allows NightOps to manage compute resources"
permissions {
actions = [
"Microsoft.Compute/virtualMachines/read",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/deallocate/action",
"Microsoft.Compute/virtualMachines/powerOff/action",
"Microsoft.Compute/virtualMachineScaleSets/read",
"Microsoft.Compute/virtualMachineScaleSets/write",
"Microsoft.Sql/servers/databases/read",
"Microsoft.Sql/servers/databases/pause/action",
"Microsoft.Sql/servers/databases/resume/action",
"Microsoft.ContainerService/managedClusters/read",
"Microsoft.ContainerService/managedClusters/agentPools/read",
"Microsoft.ContainerService/managedClusters/agentPools/write",
"Microsoft.Resources/subscriptions/resourceGroups/read"
]
}
assignable_scopes = [data.azurerm_subscription.current.id]
}
# Role Assignment
resource "azurerm_role_assignment" "nightops" {
scope = data.azurerm_subscription.current.id
role_definition_id = azurerm_role_definition.nightops.role_definition_resource_id
principal_id = azuread_service_principal.nightops.object_id
}
output "tenant_id" {
value = data.azuread_client_config.current.tenant_id
}
output "subscription_id" {
value = data.azurerm_subscription.current.subscription_id
}
output "client_id" {
value = azuread_application.nightops.client_id
}
ARM Template
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"nightopsOrgId": {
"type": "string",
"metadata": {
"description": "NightOps organization ID from dashboard"
}
}
},
"variables": {
"roleDefinitionId": "[guid(subscription().id, 'NightOps Resource Manager')]"
},
"resources": [
{
"type": "Microsoft.Authorization/roleDefinitions",
"apiVersion": "2022-04-01",
"name": "[variables('roleDefinitionId')]",
"properties": {
"roleName": "NightOps Resource Manager",
"description": "Allows NightOps to manage compute resources",
"type": "CustomRole",
"permissions": [
{
"actions": [
"Microsoft.Compute/virtualMachines/read",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/deallocate/action",
"Microsoft.Compute/virtualMachineScaleSets/read",
"Microsoft.Compute/virtualMachineScaleSets/write",
"Microsoft.Sql/servers/databases/read",
"Microsoft.Sql/servers/databases/pause/action",
"Microsoft.Sql/servers/databases/resume/action",
"Microsoft.ContainerService/managedClusters/read",
"Microsoft.ContainerService/managedClusters/agentPools/read",
"Microsoft.ContainerService/managedClusters/agentPools/write"
]
}
],
"assignableScopes": [
"[subscription().id]"
]
}
}
]
}
Deploy:
az deployment sub create \
--location eastus \
--template-file nightops-role.json \
--parameters nightopsOrgId="your-org-id"
Troubleshooting
"Unauthorized" on Test Connection
- Verify the service principal has the role assigned
- Check the federated credential configuration
- Ensure the subscription ID is correct
# Check role assignments
az role assignment list \
--assignee $APP_ID \
--scope "/subscriptions/$SUBSCRIPTION_ID"
"Invalid tenant" error
- Verify the tenant ID is correct
- Check the app registration is in the correct tenant
- Ensure federated credential issuer matches
# Verify app registration
az ad app show --id $APP_ID
VMs not discovered
Ensure the role includes Microsoft.Compute/virtualMachines/read:
az role definition list \
--name "NightOps Resource Manager" \
--query "[0].permissions[0].actions"
Security Best Practices
- Use Federated Credentials — No secrets to rotate or leak
- Custom Role — Use least-privilege custom role instead of built-in roles
- Audit Logging — Enable Azure Activity Log for the service principal
- Conditional Access — Restrict authentication based on conditions
Resource Group Scoping
Limit NightOps to specific resource groups:
az role assignment create \
--assignee $SP_OBJECT_ID \
--role "NightOps Resource Manager" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/staging-rg"
Tag-Based Filtering
While Azure RBAC doesn't support tag-based conditions natively, NightOps respects resource tags:
- Only resources tagged
nightops-managed: truewill be managed - Resources tagged
nightops-exclude: truewill be skipped