> ## Documentation Index
> Fetch the complete documentation index at: https://help.nops.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Microsoft Azure - Commitment Management Setup Script

> Set up Azure Commitment Management with one Azure CLI script: what it does, why it asks for each permission, how to run it, how to run it again, and how to fix anything it can't do for you.

## Overview

The Commitment Management setup script is the automated way to do [Run the setup](https://help.nops.io/azure/commitment-management-setup.md#run-the-setup) (steps **A** and **B**) for Azure Commitment Management. You copy one line from the nOps wizard into Azure Cloud Shell, and it downloads and runs the script. It sets up exactly what the portal steps set up, with the same template and names. Nothing more.

Choose it in the wizard at **Commitment Management → Run Setup → Run an Azure CLI script**. The other choice, **Step by step**, is the portal walkthrough. Both end at the same place, and you can switch between them at any time.

> [card] Use the script if
>
> You have several purchase subscriptions or billing profiles, or you'd rather not open each subscription and billing profile in the portal.

> [card] Use Step by step if
>
> Your organization only allows changes through the Azure portal, or a change ticket has to list each portal action.

**In short, the script:**

* Shows what it will do, and what your account is allowed to do, before it changes anything.
* Does every step your account can do, and skips the ones it can't (it doesn't fail on them).
* Ends with a numbered list of what is left. For each item it says who can do it, where in the Azure portal, and the exact command with your values filled in.
* Is safe to run again, on any day and by anyone. It checks each step, keeps what is already there, and never creates anything twice.
* **Never changes a delegation that is already there**, even one from an earlier nOps setup.
* Gives the **nOps.io** application no role at all. Purchasing access goes to nOps staff, through Azure Lighthouse, on the subscriptions you choose.

> [!NOTE]
>
> **Before you start:** [Savings Analysis](https://help.nops.io/azure/integration-setup.md) must be verified, and you must have finished **step 1** of Commitment Management (the purchase subscriptions). The script is separate from the [Savings Analysis script](https://help.nops.io/azure/cli-setup.md), so you can add Commitment Management days or weeks later.

***

## What the script sets up

| Step | What the script does | Where | Why nOps needs it |
| - | - | - | - |
| **1** | Adds the **nOps Commitment Management** delegation (Azure Lighthouse). It gives the nOps Customer Access Operators group, in the nOps tenant, **Reader**, **Reservation Purchaser** and **Savings plan Purchaser**. Registers the `Microsoft.ManagedServices` resource provider first if it isn't registered | Each purchase subscription from step 1 | Microsoft only lets someone buy a reservation or savings plan through a subscription where they hold a purchaser role. Reader lets nOps staff open the subscription in the Azure portal |
| **2** | Turns on the purchase policies. **EA:** **Reserved Instances** and **Add Savings Plan** on the enrollment. **MCA:** **Azure Reservation purchases** and **Azure Savings Plan** set to **Yes** on the billing profile each purchase subscription bills to | Your EA enrollment, or each MCA billing profile | With a policy off, Azure refuses every purchase, even from a purchaser |

The delegation uses the same template, from the same address, and the same deployment name (`nops-commitment-management`) as **Deploy to Azure** and the Cloud Shell command in **Step by step**. You see it under **Service providers** in the Azure portal, where you can remove it at any time.

### Exactly the same access as the manual setup

| Who gets it | Role | Scope | Manual step |
| - | - | - | - |
| nOps staff (Customer Access Operators group, nOps tenant) | Reader | Each purchase subscription | A |
| nOps staff | Reservation Purchaser | Each purchase subscription | A |
| nOps staff | Savings plan Purchaser | Each purchase subscription | A |
| nOps.io application | Nothing new | | |

It never assigns Owner, Contributor, User Access Administrator, any billing role, any tenant-level reservation or savings plan role, or anything on the export storage account. Purchase policies are the only billing setting it touches, and only from off to on. nOps tests this on every change to the script. For every role ID and what it allows, see [Permissions & Resources](https://help.nops.io/azure/permissions-and-resources.md#permissions-commitment-management).

### What the script never does

* **It never changes an existing delegation.** Redeploying a Lighthouse delegation replaces its roles, and some earlier nOps setups gave different roles. The script reports what such a delegation gives and prints the command that would update it, for you to decide. See [Delegations already in place](#delegations-already-in-place).
* It never turns a purchase policy off, and never changes any other policy setting. It reads the policies, changes only a purchase policy that is off, and writes every other setting back as it was. It prints the old values.
* It never deletes anything.
* It never buys anything. nOps staff buy later, in the Azure portal, through the delegation.
* It never stores or sends your Azure credentials. It runs as you, in your own Azure CLI session.

***

## How it works

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
flowchart LR
  subgraph You["Your side"]
    U["You, in Azure Cloud Shell<br/>(signed in as yourself)"]
  end
  subgraph Tenant["Your Azure tenant"]
    subgraph Subs["Purchase subscriptions (from step 1)"]
      D["nOps Commitment Management delegation<br/>Reader, Reservation Purchaser,<br/>Savings plan Purchaser"]
    end
    P["EA enrollment / MCA billing profiles<br/>purchase policies"]
    SP["nOps.io enterprise app<br/>(from Savings Analysis, read-only)"]
  end
  subgraph N["nOps"]
    W["Setup page in nOps<br/>(live checklist)"]
    S["Script endpoint<br/>(your saved script)"]
    R["Progress endpoint<br/>(step results only)"]
    O["nOps staff, nOps tenant<br/>(Customer Access Operators)"]
  end

  W -- "saves your script<br/>when you copy the command" --> S
  U -- "0. one-line command downloads it" --> S
  U -- "1. Deploy delegation" --> D
  U -- "2. Allow purchases" --> P
  U -. "step results" .-> R
  R -.-> W
  W -- "checks every few seconds<br/>as nOps.io, read-only" --> SP
  SP -- "reads delegations and policies" --> D
  SP -- "reads policies" --> P
  O -- "buy reservations and<br/>savings plans (later)" --> D
```

1. **You copy the command.** nOps saves the script for your setup, built from what you entered in the wizard.
2. **You run it** in your own Azure CLI session. The command downloads the script, saves it as `nops-azure-cm-setup.sh` and runs it. Every change is made by your account, so Azure's activity log shows you made it.
3. **The script reports each step's result to nOps**: done, skipped, needs another role, or the Azure error code with a one-line message. That's all it sends. If nOps can't be reached, setup carries on.
4. **nOps checks your tenant on its own**, as the nOps.io application, with the read access it already has from Savings Analysis. The checklist ticks off the delegation and the purchase policies as Azure shows them.
5. **When both are in place, nOps runs Verify for you** and turns Commitment Management on. Savings Analysis stays on the whole time.

### One run, step by step

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
sequenceDiagram
  participant You
  participant Script as Setup script (your Azure CLI)
  participant Azure
  participant nOps

  Script->>You: What this script does (the plan)
  Script->>Azure: Checks: signed in, right tenant, sign-in still valid, CLI version
  Script->>Azure: Which billing profile does each purchase subscription bill to?
  Script->>Azure: What can your account do? (Owner on each subscription)
  Script->>You: [yes] / [needs] per step. Start the setup now? [Y/n]
  loop Each purchase subscription
    Script->>Azure: Is the nOps delegation there, and with which roles?
    alt Today's roles
      Script->>You: [ok] … (already in place, kept)
    else Other roles
      Script->>You: [ok] + [note], or [left] … (update command listed)
    else Missing, and you are Owner
      Script->>Azure: Register Microsoft.ManagedServices, deploy the template
      Script->>You: [done] …
    else Missing, not Owner
      Script->>You: [needs] … (added to the to-do list)
    end
  end
  loop Each enrollment or billing profile
    Script->>Azure: Read the purchase policies
    alt Already allowed
      Script->>You: [ok] …
    else Off
      Script->>Azure: Write them back with purchases on
      Script->>You: [done] … (old values printed)
    end
  end
  Script-->>nOps: Step results (best effort)
  Script->>You: Summary: kept, done, and how to finish the rest
  nOps->>Azure: Checks the tenant as nOps.io
  nOps->>nOps: Both found: runs Verify
```

***

## Before you start

### Who should run it

The script does what the signed-in account is allowed to do. One person with both roles below finishes in one run. Otherwise, run it as one person, then have the next person run the **same script**. Steps already done are kept.

| Step | Role needed | Where |
| - | - | - |
| 1. Delegation | **Owner** on the subscription (it assigns roles, so Contributor isn't enough) | Each purchase subscription |
| 2. Purchase policies | **EA:** Enterprise Administrator. **MCA:** billing profile owner or contributor (or billing account owner) | The enrollment, or each billing profile |

The script checks Owner before it starts, so a **\[needs]** there is certain. For the purchase policies, Azure doesn't tell an account in advance whether a billing role reaches it through a group, so the script tries them: if Azure refuses, the step becomes **\[needs]** and goes on the to-do list.

### Three ways to run it

| Way | What you do |
| - | - |
| **Azure Cloud Shell** (recommended) | Click the copy icon next to the `curl` command, paste it into [Cloud Shell (Bash)](https://shell.azure.com/bash) and press Enter. It downloads your script from nOps, saves it as `nops-azure-cm-setup.sh`, and runs it. |
| **Paste the script** | Click **Copy setup script** and paste the whole script. Use this when the machine can't reach nOps, or to read every line as you paste. |
| **Download it** | Click **Download**, then run `bash nops-azure-cm-setup.sh` in a terminal where the Azure CLI 2.37 or later is signed in to your tenant, or send the file to whoever has the access. |

All three run the same script. Turn on **Preview only** to get the preview the same three ways.

### What to check in nOps first

* **Purchase subscriptions** (step 1): one per billing profile you want nOps to buy for (one in total for EA). The script uses exactly these.
* **Billing account ID** (MCA, in Run Setup): filled in from Savings Analysis. The script uses it to find each subscription's billing profile when step 1 doesn't name one. Correct it there if it's wrong; the change is used for the script only.

***

## Run it

**1. Open Run Setup**

After step 1, Run Setup opens on **Run an Azure CLI script**.

**2. Optional: run the preview**

Turn on **Preview only**, click the copy icon next to the command and paste it into Cloud Shell. The preview runs every check and lists exactly what the real script would change, including which delegations it would keep as they are. It changes nothing and sends nothing to nOps. Turn **Preview only** off again for the real run.

**3. Copy the curl command**

Click the copy icon next to the `curl` command. You can also expand **Read the script** to see every line first. From now on, the nOps setup page watches your tenant.

**4. Paste it into Azure Cloud Shell**

Paste and press Enter. The script prints what it will do, what your account can do, and then asks **Start the setup now? \[Y/n]**. Nothing has changed up to this point. Each delegation takes Azure about a minute, and the script says so ("Adding the delegation on … (Azure takes about a minute)...") while it waits. To run it again later, run `bash nops-azure-cm-setup.sh` in the same Cloud Shell.

**5. Read the summary**

The script ends with what was already in place, what this run did, any delegation it kept with other roles, and, if anything is left, a numbered list of how to finish it.

**6. nOps finishes on its own**

Leave the wizard open. When Azure shows the delegation and the purchase policies (usually a few minutes), nOps runs Verify and turns Commitment Management on. **Already set up? Verify now** runs it right away.

***

## Reading the output

| Tag | Meaning |
| - | - |
| `[yes]` | Before the steps: your account can do this step |
| `[needs]` | Your account doesn't have the role for this: Azure said so before the steps, or refused the change. Skipped, and added to the to-do list at the end |
| `[?]` | Azure didn't say whether your account can do this. The script tries it |
| `[done]` | Done in this run |
| `[ok] … (already in place, kept)` | Found from an earlier run, or set up by hand. Left exactly as it is |
| `[left]` | A delegation with other roles that don't include both purchaser roles. nOps left it alone, and it's on the to-do list for you to decide |
| `[fail]` | Azure returned an error. The Azure message is shown just above it, and the item is added to the to-do list |
| `[note]` | Information only (for example, a purchase policy with a value nOps doesn't change) |
| `[would]` | Preview only: what the setup script would change |

The summary always ends with one of:

* **Setup is done.** nOps finishes on its own.
* **A numbered list.** Each item says what is left, who can do it, the portal path, and the exact Azure CLI command with your values. Do them, or send the list to the person it names, then run the same script again.

***

## Delegations already in place

Some customers have a delegation from an earlier nOps setup ("nOps FinOps Management (Dedicated Subscription)"). It has the same name in Azure as today's delegation, and deploying the template again would replace its roles. So the script never redeploys one that is already there. It reads what the delegation gives nOps staff and:

| What it finds | What the script does | Counts as set up? |
| - | - | - |
| Today's roles (Reader, Reservation Purchaser, Savings plan Purchaser) | `[ok]`, kept | Yes |
| Other roles that include **both** purchaser roles (for example, with Cost Management Contributor and no Reader) | `[ok]` and a `[note]` with what it gives, kept, and listed under **Kept as they are** with the command that would update it | Yes |
| Other roles **without** both purchaser roles | `[left]`, kept, and added to the to-do list with the command that would update it | No: nOps can't buy there until it's updated |
| Today's roles, but the deployment never finished | Deployed again (no role changes) | Once Azure shows it |
| A different nOps delegation that gives both purchaser roles | `[ok]`, kept, no new delegation added | Yes |

Updating a delegation is your decision. The update command replaces its roles with today's three. If Azure rejects the update, remove the old delegation under **Service providers** in the Azure portal, then run the script again.

***

## Running it again

Run it as often as you like: after fixing something by hand, as another person, or after adding a purchase subscription in step 1 (copy the command again so the script includes it). Every run:

* reads the delegation on each purchase subscription and the purchase policies on each enrollment or billing profile before changing anything;
* keeps what is in place, and only does what is left;
* never deploys a delegation on a subscription that already has one with other roles;
* writes a purchase policy only when a purchase setting is off.

***

## What the script sends to nOps

The same as the [Savings Analysis script](https://help.nops.io/azure/cli-setup.md#what-the-script-sends-to-nops): for each step, done, skipped, needs another role, or failed, with the Azure error code and a one-line message. It sends no credentials, tokens, resource contents, cost data or personal data.

The script and the one-line command use one single-purpose token. It can post step results for your integration and download the script you saved, nothing else. It expires after 3 days, and nOps stores it only as a hash. The preview never sends anything.

***

## Troubleshooting

### Before any change is made

> [accordion] The command prints nothing, or curl reports an error (401, 404)
>
> * **401:** the command is more than 3 days old. Copy it again from nOps.
> * **404:** nOps hadn't saved the script yet (the copy failed, or nOps showed an error under the command). Copy it again.
> * **No connection:** the machine can't reach nOps through a firewall or proxy. Use Azure Cloud Shell, or use **Copy setup script** or **Download** instead.

> [accordion] "The Azure CLI isn't installed here" or "needs 2.37 or later"
>
> Run it in [Azure Cloud Shell](https://shell.azure.com/bash), which is always current, or install or upgrade the Azure CLI (`az upgrade`).

> [accordion] "You're signed in to tenant …, but nOps is connected to …"
>
> Run `az login --tenant <the tenant ID the script shows>`, then run the script again. In Cloud Shell, switch directory first (Cloud Shell → Settings → Switch directory).

> [accordion] "Your Azure sign-in … has expired or needs to be renewed"
>
> Run `az login --tenant <your tenant ID>` (it may ask for MFA), then run the script again.

> [accordion] "To build your script, add: …" in nOps
>
> The wizard is missing a value the script needs: a purchase subscription in step 1, the billing account ID (MCA), or the enrollment number (EA, from Get Started). Add it and copy again.

### During the steps

> [accordion] "… can't see purchase subscription …"
>
> The subscription ID in step 1 is wrong, or your account has no access to it. Check the subscription in step 1, or ask its Owner to run the script.

> [accordion] Delegation: \[needs] Owner on that subscription
>
> Deploying a Lighthouse delegation assigns roles, so it needs **Owner** on the subscription. Ask the subscription's Owner to run the same script, or to follow the to-do item (Deploy to Azure, or the command shown).

> [accordion] Delegation: \[left] … needs both purchaser roles
>
> The subscription has an earlier nOps delegation with roles that don't let nOps staff buy. See [Delegations already in place](#delegations-already-in-place). Its Owner runs the update command from the to-do list, or removes the old delegation under **Service providers** and runs the script again.

> [accordion] "Couldn't read which billing profile subscription … bills to"
>
> MCA only. The subscription was entered without a billing profile (nOps couldn't list your billing profiles, so step 1 showed a plain list, or it's a row saved earlier), and the script couldn't look it up in your billing account, so it didn't check that billing profile's purchase policies. Its billing profile is on the subscription's **Overview** page in the Azure portal.
>
> * In nOps step 1, tick that billing profile and enter the subscription under its row, then copy the command again and run it.
> * If step 1 still shows a plain list, ask someone with billing access to turn the policies on by hand, as the to-do item at the end of the script explains.

> [accordion] Purchase policies: \[needs] Enterprise Administrator / billing profile owner
>
> Azure refused the policy change for your account. Ask an Enterprise Administrator (EA) or a billing profile owner (MCA) to run the same script, or to follow the to-do item: **Cost Management + Billing → Billing scopes →** the enrollment or billing profile **→ Policies**.

> [accordion] "… is "…". nOps left it alone; it must allow purchases."
>
> A purchase policy has a value nOps doesn't recognize, so the script didn't change it. Check it in the portal under **Policies** and set it to allow purchases.

> [accordion] MissingSubscriptionRegistration
>
> The script registers `Microsoft.ManagedServices` and retries on its own. If it still fails, run `az provider register --namespace Microsoft.ManagedServices --subscription <id> --wait` and run the script again.

### On the nOps setup page

> [accordion] The script said Setup is done, but the checklist hasn't moved
>
> Azure can take a few minutes to show a new delegation. nOps keeps checking for 30 minutes after you copy the command; click **Check now** to check again after that.

> [accordion] Verify failed after the script finished
>
> Open Verify to see which subscription and which check failed; it names the same parts the script sets up. Fix it (or run the script again), then click **Verify Permissions**. Savings Analysis stays on either way.

***

## Undoing the setup

* **Delegation:** in the Azure portal, open **Service providers → Service provider offers**, select **nOps Commitment Management**, and delete it, or ask nOps to remove it. Removing it stops nOps staff from buying through that subscription. Commitments already bought keep applying.
* **Purchase policies:** set them back under **Cost Management + Billing → Billing scopes →** the enrollment or billing profile **→ Policies**. The script printed the old values when it changed them.

***

## FAQs

> [accordion] Does the script buy anything?
>
> No. It only sets up access. nOps staff buy later, after Verify, when nOps finds savings.

> [accordion] Why does the script use Azure Lighthouse instead of a role for nOps.io?
>
> nOps staff buy in the Azure portal, signed in to the nOps tenant with their own accounts. Azure Lighthouse is how a service provider's people get roles in a customer subscription, and each delegation is visible and removable under **Service providers**. The nOps.io application stays read-only.

> [accordion] Why one subscription per billing profile?
>
> Microsoft only lets someone buy through a subscription where they hold a purchaser role, and the subscription's billing profile decides who pays and which subscriptions a shared commitment covers. See [Commitment Management Setup](https://help.nops.io/azure/commitment-management-setup.md#faqs).

> [accordion] Can I review the script before running it?
>
> Yes. Expand **Read the script** in the wizard, click **Download** and open the file, or open `nops-azure-cm-setup.sh` after the command saves it. The **preview** makes no changes and lists exactly what would change.

> [accordion] Can I run it in a pipeline?
>
> Yes. When there's no terminal it doesn't ask for confirmation. You can also set `NOPS_SETUP_YES=1` to skip the question. Run the preview first and review its output in your change process.

> [accordion] Does it change anything for Savings Analysis?
>
> No. It doesn't touch the management group, the export storage account, the FOCUS export or nOps.io's roles. Savings Analysis stays on while you set up Commitment Management.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.