Azure
Connect your Azure subscriptions to OpsLyft optimization.
Contents
- Introduction
- Values
- Choose your path
- Integration Steps
- Set up your terminal
- Share with us
- Reference commands
- Troubleshooting
- Remove access
Introduction
OpsLyft reads resource configuration, tags and Azure Monitor metrics on a regular schedule. It is read-only and never reads application data (disks, blobs, database rows, logs).
You register OpsLyft's application (service principal) in your tenant and grant it a custom role (*/read, blob data excluded), on the subscriptions you list or on your root management group.
Values
| Name | Value |
|---|---|
| OpsLyft application ID | 01e5a9c2-d25f-4468-9875-12789b94ef71 |
| Suggested role name | OpsLyftFetchUsageMetricsRole |
Choose your path
OpsLyft does not have access to your tenant yet: follow New integration with OpsLyft. OpsLyft already has access and you want to check or update it: follow Existing integration with OpsLyft.
Each path has two variants: specific subscriptions, or the root management group (all current and future subscriptions).
Integration Steps
New integration with OpsLyft
Specific subscriptions
Covers only the subscriptions you list.
-
Open Azure Cloud Shell (Bash) in the Azure portal, or sign in with
az login. -
List the subscriptions OpsLyft should read. Find the IDs with List your subscriptions and replace the bracketed IDs by hand:
SUBSCRIPTIONS=(<SUBSCRIPTION_ID_1> <SUBSCRIPTION_ID_2>) -
Run Check your directory rights and Check your subscription rights.
-
Check that nothing exists yet: run Check that OpsLyft's application is registered, Check that the role name is free and Check the role assignments on the subscriptions.
Result: the app is reported as not found, the role check prints nothing and no subscription lists an assignment. If one prints a value, follow Existing integration with OpsLyft.
-
Register OpsLyft's application in your tenant:
az ad sp create --id "$OPSLYFT_APP_ID"Result: run Check that OpsLyft's application is registered; it prints a row with the application ID. If not, see Troubleshooting.
-
Create the custom role on every subscription in
$SUBSCRIPTIONS:SCOPES=$(for sub in "${SUBSCRIPTIONS[@]}"; do printf '"/subscriptions/%s",' "$sub"; done); SCOPES="[${SCOPES%,}]" az role definition create --role-definition "$(cat <<EOF { "properties": { "roleName": "$ROLE_NAME", "description": "OpsLyft optimization read-only role", "assignableScopes": $SCOPES, "permissions": [{ "actions": ["*/read"], "notActions": [], "dataActions": [], "notDataActions": ["Microsoft.Storage/storageAccounts/blobServices/containers/blobs/*"] }] } } EOF )"Result: run Show the role; it prints
actionsas["*/read"]and every subscription underScopes. If not, see Troubleshooting. -
Assign the role to each subscription:
for sub in "${SUBSCRIPTIONS[@]}"; do az role assignment create --assignee "$OPSLYFT_APP_ID" \ --role "$ROLE_NAME" --scope "/subscriptions/$sub" doneResult: run Check the role assignments on the subscriptions; it lists
/subscriptions/<id>for each subscription. If not, see Troubleshooting. -
Then go to Share with us.
Root management group
Covers all current and future subscriptions under your root management group.
-
Open Azure Cloud Shell (Bash) in the Azure portal, or sign in with
az login. -
Get your root management group ID. If the output is empty, ask your Azure administrator:
export ROOT_MANAGEMENT_GROUP_ID="$(az account management-group list --query '[0].name' --output tsv)"; echo "$ROOT_MANAGEMENT_GROUP_ID" -
Run Check the root management group, Check your directory rights and Check your management group rights.
-
Check that nothing exists yet: run Check that OpsLyft's application is registered, Check that the role name is free and Check the role assignment on the management group.
Result: the app is reported as not found, and the role check and the assignment check print nothing. If one prints a value, follow Existing integration with OpsLyft.
-
Register OpsLyft's application in your tenant:
az ad sp create --id "$OPSLYFT_APP_ID"Result: run Check that OpsLyft's application is registered; it prints a row with the application ID. If not, see Troubleshooting.
-
Create the custom role at management group scope:
az role definition create --role-definition "$(cat <<EOF { "properties": { "roleName": "$ROLE_NAME", "description": "OpsLyft optimization read-only role", "assignableScopes": ["/providers/Microsoft.Management/managementGroups/$ROOT_MANAGEMENT_GROUP_ID"], "permissions": [{ "actions": ["*/read"], "notActions": [], "dataActions": [], "notDataActions": ["Microsoft.Storage/storageAccounts/blobServices/containers/blobs/*"] }] } } EOF )"Result: run Show the role; it prints
actionsas["*/read"]and the management group underScopes. If not, see Troubleshooting. -
Assign the role at the management group:
az role assignment create \ --assignee "$OPSLYFT_APP_ID" \ --role "$ROLE_NAME" \ --scope "/providers/Microsoft.Management/managementGroups/$ROOT_MANAGEMENT_GROUP_ID"Result: run Check the role assignment on the management group; it lists your role at the management group. If not, see Troubleshooting.
-
Then go to Share with us.
Existing integration with OpsLyft
Checks what is already in your tenant and brings it up to date. Nothing is removed. If no role is found, follow New integration with OpsLyft.
Check current integration state
-
Open Azure Cloud Shell (Bash) in the Azure portal, or sign in with
az login. -
List the subscriptions to check. Find the IDs with List your subscriptions and replace the bracketed IDs by hand:
SUBSCRIPTIONS=(<SUBSCRIPTION_ID_1> <SUBSCRIPTION_ID_2>) -
Run Check that OpsLyft's application is registered.
Result: a row with the application ID. If the app is not found, follow New integration with OpsLyft.
-
Find the role and where it is assigned: run Find the role assignments.
Result: the
Role(its name) and theScope:/subscriptions/<id>or the management group. Nothing printed means no role: follow New integration with OpsLyft. -
Set the role name to the one printed. If several roles are printed, repeat this check and the update once per role:
export ROLE_NAME="<role name printed above>"; echo "$ROLE_NAME" -
Look at the role: run Show the role.
Result:
actionsis["*/read"],notActionsanddataActionsare[],notDataActionsis["Microsoft.Storage/storageAccounts/blobServices/containers/blobs/*"], andScopeslists every subscription OpsLyft should read. If all match, there is nothing to update: go to Share with us. Otherwise continue with the update that matches the scope you found.
Update: specific subscriptions
$SUBSCRIPTIONS must list every subscription OpsLyft should read, including those the role already covers.
-
Update the role.
nameanddescriptionare read from the existing role:SCOPES=$(for sub in "${SUBSCRIPTIONS[@]}"; do printf '"/subscriptions/%s",' "$sub"; done); SCOPES="[${SCOPES%,}]" az role definition update --role-definition "$(cat <<EOF { "name": "$(az role definition list --name "$ROLE_NAME" --query '[0].name' --output tsv)", "roleName": "$ROLE_NAME", "description": "$(az role definition list --name "$ROLE_NAME" --query '[0].description' --output tsv)", "assignableScopes": $SCOPES, "permissions": [{ "actions": ["*/read"], "notActions": [], "dataActions": [], "notDataActions": ["Microsoft.Storage/storageAccounts/blobServices/containers/blobs/*"] }] } EOF )"Result: run Show the role; it prints the expected values. If not, see Troubleshooting.
-
Assign the role to each subscription that does not have it:
for sub in "${SUBSCRIPTIONS[@]}"; do az role assignment create --assignee "$OPSLYFT_APP_ID" \ --role "$ROLE_NAME" --scope "/subscriptions/$sub" doneResult: run Check the role assignments on the subscriptions; it lists
/subscriptions/<id>for each subscription.RoleAssignmentExistsis not a failure. -
Then go to Share with us.
Update: root management group
-
Get your root management group ID. If the output is empty, ask your Azure administrator:
export ROOT_MANAGEMENT_GROUP_ID="$(az account management-group list --query '[0].name' --output tsv)"; echo "$ROOT_MANAGEMENT_GROUP_ID" -
Update the role.
nameanddescriptionare read from the existing role:az role definition update --role-definition "$(cat <<EOF { "name": "$(az role definition list --name "$ROLE_NAME" --query '[0].name' --output tsv)", "roleName": "$ROLE_NAME", "description": "$(az role definition list --name "$ROLE_NAME" --query '[0].description' --output tsv)", "assignableScopes": ["/providers/Microsoft.Management/managementGroups/$ROOT_MANAGEMENT_GROUP_ID"], "permissions": [{ "actions": ["*/read"], "notActions": [], "dataActions": [], "notDataActions": ["Microsoft.Storage/storageAccounts/blobServices/containers/blobs/*"] }] } EOF )"Result: run Show the role; it prints the expected values. If not, see Troubleshooting.
-
Assign the role at the management group if it is not there yet:
az role assignment create \ --assignee "$OPSLYFT_APP_ID" \ --role "$ROLE_NAME" \ --scope "/providers/Microsoft.Management/managementGroups/$ROOT_MANAGEMENT_GROUP_ID"Result: run Check the role assignment on the management group; it lists your role at the management group.
RoleAssignmentExistsis not a failure. -
Then go to Share with us.
Update the role permissions later
If OpsLyft sends updated role permissions, replace the permissions block below with the one OpsLyft sent, then run it. name, description and assignableScopes are read from the current role, and the assignments stay. $ROLE_NAME must be set.
az role definition update --role-definition "$(cat <<EOF
{
"name": "$(az role definition list --name "$ROLE_NAME" --query '[0].name' --output tsv)",
"roleName": "$ROLE_NAME",
"description": "$(az role definition list --name "$ROLE_NAME" --query '[0].description' --output tsv)",
"assignableScopes": $(az role definition list --name "$ROLE_NAME" --query '[0].assignableScopes' --output json),
"permissions": [{
"actions": ["*/read"], "notActions": [], "dataActions": [],
"notDataActions": ["Microsoft.Storage/storageAccounts/blobServices/containers/blobs/*"]
}]
}
EOF
)"Result: run Show the role permissions; it prints the new permissions.
Set up your terminal
Use a bash terminal or Azure Cloud Shell. Run these once per terminal. If your terminal disconnects, run them again and set SUBSCRIPTIONS or ROOT_MANAGEMENT_GROUP_ID again.
-
Only in your own terminal (not needed in Azure Cloud Shell): Check that you are signed in
-
Set the default variables. Do not change
OPSLYFT_APP_ID.ROLE_NAMEis the role you create; change it if you prefer another name or the name is taken:export OPSLYFT_APP_ID="01e5a9c2-d25f-4468-9875-12789b94ef71"; echo "$OPSLYFT_APP_ID" export ROLE_NAME="OpsLyftFetchUsageMetricsRole"; echo "$ROLE_NAME" export TENANT_ID="$(az account show --query tenantId --output tsv)"; echo "$TENANT_ID" export MY_ID="$(az ad signed-in-user show --query id --output tsv)"; echo "$MY_ID"If
TENANT_IDorMY_IDis empty, sign in again (see theAADSTS700082row in Troubleshooting) and repeat.
Share with us
Send OpsLyft these values. Send only the rows that apply to you:
| Field | What to send | How to get it |
|---|---|---|
| Path followed | New integration with OpsLyft or Existing integration with OpsLyft | Enter manually |
| Tenant ID | Your tenant (directory) ID | Run echo "$TENANT_ID" |
| Application ID | The application you granted | Listed in Values |
| Subscription IDs | The subscriptions you granted (specific subscriptions) | Run echo "${SUBSCRIPTIONS[@]}" |
| Root management group ID | Only for the root management group variant | Run echo "$ROOT_MANAGEMENT_GROUP_ID" |
When something changes later, send an update to the same table.
Reference commands
Shows the subscription and tenant you are signed in to.
az account show --query '{subscriptionId:id, tenantId:tenantId}' --output tablePrints your subscription ID and tenant ID. If az says "Please run 'az login'", run az login.
Shows your directory roles. You need a member account (not a guest) with the Application Administrator, Cloud Application Administrator or Global Administrator role.
az rest --method get --url "https://graph.microsoft.com/v1.0/me/memberOf/microsoft.graph.directoryRole" \
--query "value[].displayName" --output tsvPrints your directory roles; one of those three must be listed. If not, ask your Azure administrator to run the registration or grant you a role. A sign-in name containing #EXT# is a guest account, which cannot register it.
Shows your roles on each subscription in $SUBSCRIPTIONS. Uses $MY_ID.
for sub in "${SUBSCRIPTIONS[@]}"; do
echo "== $sub"
az role assignment list --assignee "$MY_ID" \
--include-inherited --include-groups --scope "/subscriptions/$sub" \
--query '[].roleDefinitionName' --output tsv
donePrints your roles on each subscription. Owner or User Access Administrator must be listed (a message "Failed to query ... by invoking Graph API" above it is harmless). If neither is, ask your Azure administrator to run the steps or grant you the role.
Checks that the root management group exists. Uses $ROOT_MANAGEMENT_GROUP_ID.
az account management-group show --name "$ROOT_MANAGEMENT_GROUP_ID" --query '{groupId:id, name:displayName}' --output tablePrints one row.
Shows your roles on the root management group. Uses $MY_ID and $ROOT_MANAGEMENT_GROUP_ID.
az role assignment list --assignee "$MY_ID" \
--include-inherited --include-groups \
--scope /providers/Microsoft.Management/managementGroups/$ROOT_MANAGEMENT_GROUP_ID \
--query '[].roleDefinitionName' --output tsvPrints your roles on the management group. Owner or User Access Administrator must be listed; if neither is, ask your Azure administrator to run the steps or grant you the role.
Lists the subscriptions in your tenant.
az account list --query "[?tenantId=='$(az account show --query tenantId --output tsv)'].{name:name, subscriptionId:id}" --output tablePrints name and subscription ID of each subscription in your tenant. Subscriptions of other tenants from other sign-ins fail with InvalidAuthenticationTokenTenant.
Checks that OpsLyft's application is in your tenant. Uses $OPSLYFT_APP_ID.
az ad sp show --id "$OPSLYFT_APP_ID" --query '{appId:appId, name:displayName}' --output tablePrints a row with the application ID. An error saying the resource does not exist means it is not registered yet.
Checks that no role with that name exists. Uses $ROLE_NAME.
az role definition list --name "$ROLE_NAME" --query '[].roleName' --output tsvPrints nothing when the name is free. If it prints the name, the role already exists: follow Existing integration with OpsLyft, or set another ROLE_NAME.
Checks that the role is assigned on each subscription. Uses $OPSLYFT_APP_ID, $ROLE_NAME and $SUBSCRIPTIONS.
for sub in "${SUBSCRIPTIONS[@]}"; do
echo "== $sub"
az role assignment list --assignee "$OPSLYFT_APP_ID" --subscription "$sub" \
--query "[?roleDefinitionName=='$ROLE_NAME'].scope" --output tsv
donePrints /subscriptions/<id> under each subscription that has the role, and nothing under one that does not.
Checks that the role is assigned at the management group. Uses $OPSLYFT_APP_ID, $ROLE_NAME and $ROOT_MANAGEMENT_GROUP_ID.
az role assignment list --assignee "$OPSLYFT_APP_ID" \
--scope "/providers/Microsoft.Management/managementGroups/$ROOT_MANAGEMENT_GROUP_ID" \
--query '[].{Role:roleDefinitionName, Scope:scope}' --output tablePrints the role and scope; your role is listed when it is assigned, and nothing prints when it is not.
Shows the role and its scopes. Uses $ROLE_NAME.
az role definition list --name "$ROLE_NAME" \
--query '[].{Scopes:assignableScopes, Permissions:permissions[0]}' --output jsonPrints Scopes (the subscriptions or the management group) and the permission fields actions, notActions, dataActions and notDataActions.
Finds the role and where it is assigned. Uses $OPSLYFT_APP_ID and $SUBSCRIPTIONS.
for sub in "${SUBSCRIPTIONS[@]}"; do
echo "== $sub"
az role assignment list --assignee "$OPSLYFT_APP_ID" --subscription "$sub" --include-inherited \
--query '[].{Role:roleDefinitionName, Scope:scope}' --output table
donePrints per subscription the Role (the role name to set) and the Scope (a subscription or the management group).
Shows the permissions of the role. Run it after an update. Uses $ROLE_NAME.
az role definition list --name "$ROLE_NAME" --query '[].permissions[0]' --output jsonPrints the permissions of the role.
Troubleshooting
| Problem | Fix |
|---|---|
AuthorizationFailed on role creation or assignment | You need Owner or User Access Administrator (see Check your subscription rights or Check your management group rights). Ask your Azure administrator. |
az ad sp create fails with already exists | The application is already in your tenant. Not an error, continue. |
RoleDefinitionAlreadyExists on role creation | The role exists. Run Show the role and follow Existing integration with OpsLyft, or set another ROLE_NAME. |
RoleAssignmentExists on assignment | Already assigned. Not a failure, continue. |
az ad sp show or az ad sp create prints Insufficient privileges to complete the operation | Your account is a guest or lacks a directory role. Use a member account with Application Administrator, Cloud Application Administrator or Global Administrator (see Check your directory rights), or ask your Azure administrator to run the registration. |
az reports "Please run 'az login'" | Run az login, or use Azure Cloud Shell, which is pre-authenticated. |
az prints AADSTS700082 (sign-in expired) | Run az logout, then az login --tenant "$TENANT_ID", then repeat the command. |
InvalidAuthenticationTokenTenant | The subscription belongs to another tenant. Use one from List your subscriptions. |
| No role found in Existing integration with OpsLyft | Nothing is assigned: follow New integration with OpsLyft. |
Remove access
Removing the role assignment is enough to stop OpsLyft reading; deleting the role is optional. Tell OpsLyft when you have removed access. Set $OPSLYFT_APP_ID and $ROLE_NAME first, plus $SUBSCRIPTIONS or $ROOT_MANAGEMENT_GROUP_ID for the level you use (see Set up your terminal). Run only the command for the level you used.
For subscriptions:
for sub in "${SUBSCRIPTIONS[@]}"; do
az role assignment delete --assignee "$OPSLYFT_APP_ID" --role "$ROLE_NAME" \
--scope "/subscriptions/$sub"
doneFor the root management group:
az role assignment delete --assignee "$OPSLYFT_APP_ID" --role "$ROLE_NAME" \
--scope "/providers/Microsoft.Management/managementGroups/$ROOT_MANAGEMENT_GROUP_ID"To delete the role afterwards (once every assignment is removed):
az role definition delete --name "$ROLE_NAME"