> ## 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 - Savings Analysis Setup

> Connect your Azure environment to nOps for read-only cost visibility, resource inventory, optimization recommendations, and daily FOCUS cost exports.

## FAQs

<AccordionGroup>
  <Accordion title="How long does it take for Azure role assignments to propagate?">
    Azure RBAC role assignments typically take **2-5 minutes** to propagate. In some cases, it may take up to 10 minutes. If verification fails immediately after completing the setup, wait a few minutes and try again.
  </Accordion>

  <Accordion title="What are FOCUS cost exports?">
    FOCUS (FinOps Open Cost and Usage Specification) is an open-source standard for cloud cost data. nOps creates a daily FOCUS export in your Azure subscription that writes billing data to a storage account. This provides structured, normalized cost data for analysis and optimization.
  </Accordion>

  <Accordion title="Why are roles assigned at the Management Group scope?">
    Assigning roles at the management group scope ensures that nOps has consistent visibility across **all subscriptions** within that management group. This avoids the need to grant permissions on each subscription individually, and any new subscriptions added to the group automatically inherit the roles.
  </Accordion>

  <Accordion title="How often is billing data refreshed?">
    FOCUS cost exports run **daily**. Cost data is typically available within 24-48 hours of the export run. After the initial setup, historical data for the current month is included in the first export.
  </Accordion>

  <Accordion title="What resources does the setup create in my subscription?">
    The setup creates three resources in your specified subscription:

    * A **Resource Group** (`nops-focus-exports-rg`) in East US
    * A **Storage Account** (`nopsfocus<tenant-prefix>`) for FOCUS cost data
    * A **FOCUS cost export** (`nops-clara-focus-export`) that runs daily

    The setup also registers three resource providers on the subscription: `Microsoft.Storage`, `Microsoft.CostManagement`, and `Microsoft.CostManagementExports`. These are required for storage account creation and FOCUS export functionality.

    These resources incur minimal cost (storage fees for the exported data only).
  </Accordion>

  <Accordion title="Can I use an existing storage account instead?">
    No. The setup creates a dedicated storage account for nOps FOCUS exports to ensure proper isolation and access control. The nOps service principal is granted the **Owner** role only on this specific storage account.
  </Accordion>

  <Accordion title="What if my organization restricts storage account network access?">
    If your organization has Azure Policies that enforce storage account firewalls, the nOps FOCUS export storage account needs the nOps ingestion IPs whitelisted.

    1. Navigate to the storage account > **Security + networking** > **Networking**
    2. Under **Firewall**, add the nOps IP addresses: `44.246.45.84`, `52.11.53.164`, and `52.39.155.200`
    3. Ensure **"Allow Azure services on the trusted services list"** is checked
    4. Click **Save**

    The Terraform setup method detects firewall restrictions automatically and prompts you to add the IPs.
  </Accordion>
</AccordionGroup>

***

## Overview

**Savings Analysis** is the first tier of Azure onboarding. It gives nOps **read-only** access to your Azure environment for:

* Cost visibility across all subscriptions
* Resource inventory (VMs, disks, networks)
* Optimization recommendations (rightsizing, idle resources)
* Daily FOCUS cost exports for cost tracking

<Info>
  **Supported Billing Account Types:** nOps supports **Enterprise Agreement (EA)** and **Microsoft Customer Agreement (MCA)** billing accounts only. If you have a Pay-As-You-Go (MOSP) subscription, please contact your Microsoft account representative to upgrade to an EA or MCA billing account before proceeding.
</Info>

<Info>
  Savings Analysis does **not** allow purchasing reservations or savings plans. To enable automated commitment purchasing, complete this setup first, then follow the [Commitment Management Setup](/azure/commitment-management-setup) guide.
</Info>

<Info>
  **Prerequisites** -- Before proceeding, ensure you have completed the [Azure Integration Prerequisites](/azure/prerequisites) including:

  * Understanding your billing account type (EA or MCA)
  * Locating your Management Group ID
  * Having a subscription available for FOCUS exports
  * **EA only:** Your EA Enrollment Number -- find it in [Cost Management + Billing](https://portal.azure.com/#blade/Microsoft_Azure_GTM/ModernBillingMenuBlade) in the Azure Portal
  * **MCA only:** A **Billing Account Owner** on the MCA billing account must complete the billing role assignment in Step B below. This is a billing-scope role, separate from Entra ID and Azure RBAC
  * Having Global Administrator or Privileged Role Administrator access
</Info>

The nOps Azure Savings Analysis wizard guides you through a 5-step process:

1. **Get Started** -- Name your integration and select your billing type
2. **Admin Consent** -- Grant nOps access to your Azure tenant
3. **Configure** -- Provide your Management Group ID and subscription
4. **Run Setup** -- Execute the RBAC and FOCUS export setup
5. **Verify** -- Validate that all permissions are correctly configured

***

## 1. Get Started

<Tip>
  **Before you start:** confirm your account is eligible. Run the [nOps onboarding qualifier](/azure/prerequisites#recommended-run-the-nops-onboarding-qualifier) -- a read-only script that classifies each billing account (**EA / MCA / Reseller / CSP**) and gives a **PASS / FAIL** verdict. CSP / partner-billed accounts are not supported, so this avoids starting a setup that cannot complete.
</Tip>

<Steps>
  <Step title="Open Cloud Provider Integrations">
    Log in to **nOps** and go to [**Settings > Cloud Provider Integrations**](https://clara.nops.io/settings). Scroll down to the **Add a provider** section and click the **Microsoft Azure** card.
  </Step>

  <Step title="Enter an Integration Name">
    Provide a friendly name for this integration (e.g., "Production Azure Environment"). This name helps you identify the integration in the nOps dashboard.
  </Step>

  <Step title="Select Your Billing Account Type">
    Choose between **EA** (Enterprise Agreement) or **MCA** (Microsoft Customer Agreement). This determines how the setup configures billing access.

    If you are unsure which type you have, see [Understanding Billing Account Types](/azure/prerequisites#1-understanding-billing-account-types).
  </Step>

  <Step title="Enter Your EA Enrollment Number (EA only)">
    If you selected **EA**, enter your **EA Enrollment Number** in this step. It is required to continue. You can change it later in **Configure**.

    Find it in [Azure Portal > Cost Management + Billing](https://portal.azure.com/#blade/Microsoft_Azure_GTM/ModernBillingMenuBlade) — select your EA account and copy the numeric enrollment ID shown at the top. MCA users skip this step.
  </Step>

  <Step title="Click Continue">
    nOps creates an integration record and generates the admin consent URL for the next step.
  </Step>
</Steps>

***

## 2. Grant Admin Consent

Admin consent creates a **service principal** for nOps in your Azure AD tenant. This is required before any roles can be assigned.

<Steps>
  <Step title="Click Grant Admin Consent">
    In the nOps wizard, click the **Grant Admin Consent** button. You will be redirected to Microsoft's login page.
  </Step>

  <Step title="Sign In with an Admin Account">
    Sign in with an Azure AD account that has the **Global Administrator** or **Privileged Role Administrator** role.
  </Step>

  <Step title="Review and Accept Permissions">
    Review the permissions requested by nOps and click **Accept**. This creates the nOps service principal in your tenant.
  </Step>

  <Step title="Return to nOps">
    After accepting, you are automatically redirected back to the nOps wizard. Your **Azure Tenant ID** is now displayed, confirming that consent was granted successfully.
  </Step>
</Steps>

### Understanding Admin Consent

Admin consent creates a **service principal** for nOps in your Azure AD tenant. Here's what happens during this process:

1. You click the **Grant Admin Consent** button in the nOps wizard
2. You are redirected to **Microsoft's login page**
3. You sign in with an Azure AD account that has **Global Administrator** or **Privileged Role Administrator** role
4. You review and accept the permissions requested by nOps
5. Microsoft creates a **service principal** for the nOps application in your Azure tenant
6. You are redirected back to the nOps wizard to continue setup

<Info>
  **What is a service principal?** A service principal is an identity created in your Azure AD tenant that represents the nOps application. It has no credentials of its own -- nOps authenticates using its multi-tenant application registration, and Azure AD validates the request against the service principal in your tenant.
</Info>

<Warning>
  **Do not remove the service principal** from your Azure AD tenant after granting consent. If removed, nOps will lose access and you will need to re-grant admin consent from the wizard.
</Warning>

***

## 3. Configure Your Azure Environment

After consent is granted, provide the configuration details for your integration.

<Steps>
  <Step title="Enter Your Management Group ID">
    Enter the **Management Group ID** that defines the scope of nOps visibility. Use your **Tenant ID** to cover all subscriptions (root management group), or a specific management group ID for a subset.

    Find this in [Azure Portal > Management Groups](https://portal.azure.com/#view/Microsoft_Azure_ManagementGroups/ManagementGroupBrowseBlade).
  </Step>

  <Step title="Enter a Subscription ID for FOCUS Exports">
    Choose the subscription with the most spend. nOps will create a storage account and configure FOCUS cost exports in this subscription.

    Find your subscription IDs in [Azure Portal > Subscriptions](https://portal.azure.com/#view/Microsoft_Azure_Billing/SubscriptionsBlade).
  </Step>

  <Step title="Confirm or Edit EA Enrollment Number (EA only)">
    If you selected **Enterprise Agreement** as your billing type, confirm or edit your **EA Enrollment Number** (the numeric ID you entered in Get Started). This is required to generate the setup script.

    Find it in [Azure Portal > Cost Management + Billing](https://portal.azure.com/#blade/Microsoft_Azure_GTM/ModernBillingMenuBlade) — select your EA account and copy the enrollment number shown at the top. MCA users skip this step.
  </Step>

  <Step title="Click Continue">
    nOps saves your configuration and generates the setup script for the next step.
  </Step>
</Steps>

<Tip>
  Choose the subscription with the most spend for FOCUS exports. No dedicated subscription is required for the Savings Analysis tier. The setup creates lightweight resources with minimal cost.
</Tip>

***

## 4. Run the Setup

The nOps wizard generates a customized setup that assigns RBAC roles, creates the storage account, and configures FOCUS cost exports. You can complete the setup through the **Azure Portal** (recommended) or **Terraform**.

### What the Setup Does

The setup assigns the RBAC roles documented in [Prerequisites - RBAC Roles](/azure/prerequisites#5-understanding-rbac-roles-for-nops) and creates the following resources:

**Storage Account Setup**

| Resource        | Name                       | Purpose                                       |
| --------------- | -------------------------- | --------------------------------------------- |
| Resource Group  | `nops-focus-exports-rg`    | Container for nOps export resources (East US) |
| Storage Account | `nopsfocus<tenant-prefix>` | Stores FOCUS cost export data                 |
| FOCUS Export    | `nops-clara-focus-export`  | Daily cost export                             |

**Roles Assigned**

* **Tenant Provider scope:** Reservations Reader, Savings Plan Reader
* **Billing Account scope:** EnrollmentReader (EA only) **or** Billing Account Reader (MCA only)
* **Management Group scope:** Reader, Cost Management Contributor, Monitoring Reader
* **Storage Account scope:** Owner, Storage Blob Data Reader

<Info>
  The **Owner** role is granted exclusively on the storage account created by nOps for FOCUS exports (`nopsfocus<tenant-prefix>`) -- it does not grant Owner access to any other resources in your environment.
</Info>

For complete role definitions and purposes, see [Prerequisites - Understanding RBAC Roles for nOps](/azure/prerequisites#5-understanding-rbac-roles-for-nops).

### Running the Setup

<Tabs>
  <Tab title="Azure Portal">
    **Recommended for most users.** Complete the setup through the Azure Portal UI with visual confirmation at each step.

    <Info>
      The nOps wizard walks you through these same steps (labeled **A** through **E**). Follow along in the wizard or use the instructions below.
    </Info>

    **A. Assign Tenant-Level Reader Roles**

    These roles are assigned at the tenant provider scope so nOps can read your existing reservations and savings plans. You must be a **Global Administrator** with elevated access to assign them.

    <Warning>
      **Prerequisite:** You must enable **"Access management for Azure resources"** in Microsoft Entra ID before you can assign tenant-level roles. Go to [**Microsoft Entra ID → Properties**](https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/Properties), toggle **"Access management for Azure resources"** to **Yes**, click **Save**, then **sign out** and **sign back in** to pick up the new permissions.
    </Warning>

    <Steps>
      <Step title="Assign Reservations Reader">
        1. Go to [**Reservations**](https://portal.azure.com/#view/Microsoft_Azure_Reservations/ReservationsBrowseBlade) in the Azure Portal.
        2. Click the **Role Assignment** button in the top toolbar.
        3. Click **Add**, select the **Reservations Reader** role.
        4. Search for **"nOps.io"**, select it, then click **Save**.
      </Step>

      <Step title="Assign Savings Plan Reader">
        1. Go to [**Reservations → Savings Plans**](https://portal.azure.com/#view/Microsoft_Azure_Reservations/ReservationsBrowseBlade/productType/SavingsPlan) in the Azure Portal.
        2. Click **Role Assignment** in the top toolbar.
        3. Click **Add** → **Add role assignment**.
        4. Search for **Savings Plan Reader**, assign to **"nOps.io"**.
      </Step>
    </Steps>

    **B. Assign Billing Account Role**

    Follow **only** the path that matches your billing type. **EA:** EnrollmentReader (CLI required). **MCA:** Billing Account Reader (Portal).

    <Tabs>
      <Tab title="Enterprise Agreement (EA)">
        <Warning>
          Azure does **not** support assigning EA billing roles (such as EnrollmentReader) to service principals through the Azure Portal. These roles can only be assigned programmatically via the REST API. Use the script below in [Azure Cloud Shell](https://shell.azure.com) (Bash, not PowerShell) or a local terminal with the Azure CLI installed.
        </Warning>

        <Steps>
          <Step title="Sign in to Azure CLI">
            Open [Azure Cloud Shell](https://shell.azure.com) (Bash) or run `az login` in your local terminal.
          </Step>

          <Step title="Run the EnrollmentReader assignment script">
            Copy and run the following script. Replace `<enrollment-number>` and `<tenant-id>` with your values (the nOps wizard pre-fills these in its generated script):

            ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
            # Assign EnrollmentReader role to nOps service principal
            # Run in Azure Cloud Shell (Bash, not PowerShell) or a local terminal with Azure CLI

            NOPS_APP_CLIENT_ID="<app-client-id>"

            # Look up the nOps service principal Object ID
            NOPS_SP_OBJECT_ID=$(az ad sp show --id "$NOPS_APP_CLIENT_ID" --query "id" -o tsv 2>/dev/null || true)

            if [ -z "$NOPS_SP_OBJECT_ID" ]; then
              echo "ERROR: nOps service principal not found. Complete the Admin Consent step first."
              exit 1
            fi

            echo "Found nOps service principal: $NOPS_SP_OBJECT_ID"

            ROLE_ASSIGNMENT_GUID=$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid)

            az rest --method PUT \
              --url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<enrollment-number>/billingRoleAssignments/${ROLE_ASSIGNMENT_GUID}?api-version=2024-04-01" \
              --body '{
                "properties": {
                  "principalId": "'"$NOPS_SP_OBJECT_ID"'",
                  "principalTenantId": "<tenant-id>",
                  "roleDefinitionId": "/providers/Microsoft.Billing/billingAccounts/<enrollment-number>/billingRoleDefinitions/24f8edb6-1668-4659-b5e2-40bb5f3a7d7e"
                }
              }'

            echo "Done. A 200 OK response confirms the EnrollmentReader role was assigned."
            ```

            <Tip>
              The nOps wizard generates a ready-to-run script with all values pre-filled (App Client ID, enrollment number, and tenant ID). Copy it directly from the wizard for the easiest experience.
            </Tip>

            <Info>
              **Fallback:** If `az ad sp show` fails, you can find the service principal manually: `az ad sp list --filter "startswith(displayName, 'nops')" --query "[].{name:displayName, objectId:id}" -o table` and use the **objectId** value.
            </Info>

            A `200 OK` response confirms the role was assigned. Repeat for each billing account if you have more than one.
          </Step>
        </Steps>
      </Tab>

      <Tab title="Microsoft Customer Agreement (MCA)">
        <Warning>
          **You must be a Billing Account Owner** on the MCA billing account to complete this step. **Global Administrator** and **Azure RBAC Owner** do **not** grant billing-scope permissions. If you don't have this role, ask your Billing Account Owner to complete this step. Check who holds this role in [**Cost Management + Billing**](https://portal.azure.com/#blade/Microsoft_Azure_GTM/ModernBillingMenuBlade) → select billing account → **Access control (IAM)**.
        </Warning>

        <Steps>
          <Step title="Navigate to Billing">
            Go to [**Cost Management + Billing**](https://portal.azure.com/#blade/Microsoft_Azure_GTM/ModernBillingMenuBlade) in the Azure Portal. Select your **Microsoft Customer Agreement** billing account, then select the appropriate **Billing scope** (billing account level).
          </Step>

          <Step title="Assign Billing Account Reader">
            1. Click **Access control (IAM)** in the left menu.
            2. Click **Add** at the top.
            3. Select role: **Billing Account Reader**.
            4. On the **Members** tab, choose **"User, group, or service principal"** and search for **"nOps.io"**.
            5. Click **Review + assign**.

            Repeat for each billing account if you have more than one.
          </Step>
        </Steps>
      </Tab>
    </Tabs>

    **C. Assign RBAC Roles at Management Group Scope**

    Repeat the following steps for each role: **Reader**, **Cost Management Contributor**, and **Monitoring Reader**.

    <Steps>
      <Step title="Navigate to Management Groups">
        Go to [**Management Groups**](https://portal.azure.com/#view/Microsoft_Azure_ManagementGroups/ManagementGroupBrowseBlade) in the Azure Portal. Select the target management group (or the **Tenant Root Group** to cover all subscriptions).
      </Step>

      <Step title="Open Access Control">
        Click **Access control (IAM)** in the left menu, then click **Add** > **Add role assignment**.
      </Step>

      <Step title="Select the Role">
        On the **Role** tab, search for and select the role (e.g., **Reader**), then click **Next**.
      </Step>

      <Step title="Assign to the nOps Service Principal">
        1. On the **Members** tab, set **"Assign access to"** to **"User, group, or service principal"** (not "Managed identity").
        2. Click **"+ Select members"**.
        3. Search for **"nOps.io"** by name and select it.
        4. Select the nOps.io enterprise application and click **Select**.
        5. Click **Review + assign**.
      </Step>

      <Step title="Repeat for Remaining Roles">
        Repeat the role assignment process for each of these roles:

        * **Reader** -- View all resources across subscriptions
        * **Cost Management Contributor** -- Query cost data and manage exports
        * **Monitoring Reader** -- View monitoring metrics and diagnostics

        Each time: **Add** > **Add role assignment** > select the role > search for **"nOps.io"** > **Review + assign**.
      </Step>
    </Steps>

    **D. Create FOCUS Cost Export**

    <Warning>
      **Register Resource Providers First** -- Before creating the export, ensure these resource providers are registered on the subscription where the storage account will be created.

      **Via Portal:** Go to [**Subscriptions → Resource providers**](https://portal.azure.com/#view/Microsoft_Azure_Billing/SubscriptionsBlade) and click **Register** on each: `Microsoft.Storage`, `Microsoft.CostManagement`, `Microsoft.CostManagementExports`.

      **Via CLI:** Run in [Azure Cloud Shell](https://shell.azure.com) (Bash):

      ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
      az provider register --namespace Microsoft.Storage && \
      az provider register --namespace Microsoft.CostManagement && \
      az provider register --namespace Microsoft.CostManagementExports
      ```

      Most subscriptions already have these registered. If a provider already shows "Registered", no action is needed.
    </Warning>

    Follow **only** the path that matches your billing type (EA or MCA).

    **Path A — Enterprise Agreement (EA)**

    <Steps>
      <Step title="Navigate to Exports">
        Go to [**Cost Management > Exports**](https://portal.azure.com/#view/Microsoft_Azure_CostManagement/Menu/~/exports) in the Azure Portal.
      </Step>

      <Step title="Set the Scope to Your EA Enrollment Billing Account">
        At the top, click **Change scope** if needed. Select your **EA enrollment** billing account (use the same **EA Enrollment Number** you entered in the nOps wizard — e.g., `12345678`).
      </Step>

      <Step title="Create a New Export">
        1. Click **+ Create**.
        2. **Basics** tab: Select the **"Cost and usage (FOCUS)"** template.
        3. **Datasets** tab: Set export prefix to `nops-clara` and frequency to **Daily** (set automatically by the FOCUS template).
        4. **Destination** tab:
           * Storage type: **Azure blob storage**
           * Subscription: Select the subscription you configured in the nOps wizard
           * Storage account: Click **Create new**
             * Resource group: create new — `nops-focus-exports-rg`
             * Name: `nopsfocus` + first 15 hex characters of your tenant ID (no hyphens)
           * Container: `focus-exports`
           * Directory: `nops-clara`
           * Format: **Parquet**
           * Compression type: **Snappy**
           * File partitioning: **checked**
           * Overwrite data: **checked**
        5. Click **Review + create**, then **Create**.
      </Step>

      <Step title="Trigger the First Export and Backfill">
        1. After creation, click the export name, then click **Run now** to trigger the first export.
        2. Click **Export selected dates** to backfill the previous 3 months:
           * **Run 1:** Start date = first day of three months ago, End date = last day of three months ago
           * **Run 2:** Start date = first day of two months ago, End date = last day of two months ago
           * **Run 3:** Start date = first day of previous month, End date = last day of previous month

        Data will be available within a few hours.
      </Step>
    </Steps>

    **Path B — Microsoft Customer Agreement (MCA)**

    <Info>
      **Billing profile auto-detection:** When you reach this step for an MCA account, nOps automatically detects your billing profiles. If your account has **multiple billing profiles**, the wizard displays a selector so you can choose the primary billing profile — the export scope is then set correctly for that profile. If your account has only one billing profile, no action is needed; nOps uses it automatically.
    </Info>

    <Steps>
      <Step title="Navigate to Exports">
        Go to [**Cost Management > Exports**](https://portal.azure.com/#view/Microsoft_Azure_CostManagement/Menu/~/exports) in the Azure Portal.
      </Step>

      <Step title="Set the Scope to Your MCA Billing Profile">
        At the top, click **Change scope** if needed. Select your **Microsoft Customer Agreement** billing profile (not the billing account — MCA cost data lives at the profile level).

        <Tip>
          If you selected a billing profile in the nOps wizard, use that same billing profile here. If you have multiple billing profiles, repeat this step for each one, pointing **all exports to the same storage account** (`nopsfocus<tenant-prefix>`).
        </Tip>
      </Step>

      <Step title="Create a New Export">
        1. Click **+ Create**.
        2. **Basics** tab: Select the **"Cost and usage (FOCUS)"** template.
        3. **Datasets** tab: Set export prefix to `nops-clara` and frequency to **Daily** (set automatically by the FOCUS template).
        4. **Destination** tab:
           * Storage type: **Azure blob storage**
           * Subscription: Select the subscription you configured in the nOps wizard
           * Storage account: Click **Create new**
             * Resource group: create new — `nops-focus-exports-rg`
             * Name: `nopsfocus` + first 15 hex characters of your tenant ID (no hyphens)
           * Container: `focus-exports`
           * Directory: `nops-clara`
           * Format: **Parquet**
           * Compression type: **Snappy**
           * File partitioning: **checked**
           * Overwrite data: **checked**
        5. Click **Review + create**, then **Create**.
      </Step>

      <Step title="Trigger the First Export and Backfill">
        1. After creation, click the export name, then click **Run now** to trigger the first export.
        2. Click **Export selected dates** to backfill the previous 3 months:
           * **Run 1:** Start date = first day of three months ago, End date = last day of three months ago
           * **Run 2:** Start date = first day of two months ago, End date = last day of two months ago
           * **Run 3:** Start date = first day of previous month, End date = last day of previous month

        Data will be available within a few hours.

        <Info>
          **Multiple Billing Profiles:** If you have multiple billing profiles with spend, repeat this step for each one and point every export at the same storage account.
        </Info>
      </Step>
    </Steps>

    <Tip>
      The nOps wizard displays the exact storage account name, resource group, container, and folder path you need. Copy these values from the wizard to ensure they match the platform's validation.
    </Tip>

    **E. Assign Roles on the Storage Account**

    <Steps>
      <Step title="Navigate to the Storage Account">
        Search for **"Storage accounts"** in the Azure Portal top search bar, then select the storage account you created (e.g., `nopsfocus<tenant-prefix>`).
      </Step>

      <Step title="Assign Owner Role">
        1. Go to **Access control (IAM)** → **Add** → **Add role assignment**.
        2. On the **Role** tab, click the **"Privileged administrator roles"** tab (Owner is not under "Job function roles").
        3. Select **Owner**, click **Next**.
        4. On the **Members** tab, set **"Assign access to"** to **"User, group, or service principal"**.
        5. Click **"+ Select members"**, search for **"nOps.io"**, select and confirm.
        6. On the **Conditions** tab, select **"Allow user to assign all roles except privileged administrator roles Owner, UAA, RBAC (Recommended)"**.
        7. Click **Review + assign**.
      </Step>

      <Step title="Assign Storage Blob Data Reader">
        1. Go back to **Access control (IAM)** → **Add** → **Add role assignment**.
        2. On the **Role** tab, search for **Storage Blob Data Reader** and select it.
        3. On the **Members** tab, choose **"User, group, or service principal"** → search for **"nOps.io"**.
        4. Click **Review + assign** (no Conditions tab for this role).
      </Step>

      <Step title="Multiple billing profiles (MCA only)">
        If you created FOCUS exports for **multiple MCA billing profiles**, ensure that **Owner** and **Storage Blob Data Reader** are assigned to the nOps service principal on **each storage account** used. If all profiles share the same storage account, you only need to assign the roles once.
      </Step>

      <Step title="Configure Firewall (if applicable)">
        If your organization restricts storage account network access (via Azure Policy or manual configuration):

        1. Go to the storage account > **Security + networking** > **Networking**
        2. Under **Firewall**, add the nOps IP addresses: `44.246.45.84`, `52.11.53.164`, and `52.39.155.200`
        3. Ensure **"Allow Azure services on the trusted services list"** is checked
        4. Click **Save**

        <Info>
          Skip this step if your storage account allows public network access (the default). The nOps data ingestion service reads FOCUS export data from outside Azure, so if a firewall is enabled, the nOps IP must be whitelisted.
        </Info>
      </Step>
    </Steps>

    <Info>
      **Verification troubleshooting:** Both the **Owner** and **Storage Blob Data Reader** roles must be assigned **directly on the storage account resource** (not inherited from a higher scope such as the subscription or management group). Azure role assignments can take **2-5 minutes** to propagate. If verification fails immediately after assigning roles, wait a few minutes and try again.
    </Info>

    <Info>
      **Multiple Billing Profiles (MCA):** If you created separate FOCUS exports for multiple billing profiles, all exports should point to the same storage account — no additional role assignments are needed.
    </Info>

    **Return to nOps**

    Once you have completed all the steps above (A through E), return to the nOps wizard and click **"I've Completed the Manual Setup"** to proceed to the verification step.
  </Tab>

  <Tab title="Terraform">
    **For infrastructure-as-code workflows.** The Terraform option generates an HCL configuration file.

    1. In the nOps wizard, select **Terraform** and click **Copy Terraform Configuration**.
    2. Save the configuration as `main.tf` in a new directory.
    3. Initialize and apply:
       ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
       terraform init
       terraform plan    # Review the changes
       terraform apply   # Apply the changes
       ```
    4. Return to the nOps wizard and click **"I've Applied the Configuration"**.

    <Warning>
      **MCA Compatibility** -- Terraform may have limited support for MCA billing role assignments. Azure's billing API requires POST for MCA, but Terraform uses PUT by default. If you encounter issues with MCA, use the **Azure Portal** method instead.
    </Warning>
  </Tab>
</Tabs>

<Info>
  **Configuration Preview** -- When using the Terraform method, you can expand the configuration preview in the nOps wizard to inspect exactly what will be applied before copying it.
</Info>

***

## 4a. Minimum Permissions Setup

For security-sensitive customers (e.g., least-privilege security reviews).

If your organization's security review rejects the standard 8-role assignment, nOps offers an alternate path that grants only the **three roles** strictly required for Savings Analysis. Enable it by ticking **"Use minimum permissions (security-sensitive)"** on the **Get Started** step of the wizard.

### What changes in this flow

* The setup script section is replaced with **five portal-driven steps** — there is no Bicep or Terraform deployment to run.
* **You create the storage account and FOCUS export yourself** in the Azure Portal; nOps never needs Owner-level access on those resources.
* nOps backend verification uses a data-plane blob probe instead of `listKeys`, and probes Monitoring Reader via the Azure Monitor `metrics:getBatch` API.
* Backfills must be triggered manually from the Azure Portal (the wizard's automated backfill button is disabled in this mode).

### Roles granted

| Scope            | Role                         | Why                                                                                                   |
| ---------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------- |
| Management Group | **Reader**                   | List resources across subscriptions; covers Cost Management read via the wildcard `*/read` permission |
| Management Group | **Monitoring Reader**        | Hourly CPU metrics for reservation utilization modelling                                              |
| Storage Account  | **Storage Blob Data Reader** | Read-only data-plane access to the FOCUS Parquet files                                                |

No tenant-root elevation, no Owner, no Cost Management Contributor, no billing-scoped roles.

### Five portal steps the wizard walks you through

<Steps>
  <Step title="Assign Reader at the Management Group">
    Navigate to **Management Groups > your management group > Access control (IAM) > Add role assignment**. Select **Reader**, then assign it to the **nOps** enterprise application.
  </Step>

  <Step title="Assign Monitoring Reader at the Management Group">
    Repeat the same flow on the same management group, this time selecting **Monitoring Reader**.
  </Step>

  <Step title="Create the storage account">
    Create a **Standard / locally-redundant (LRS)** general-purpose v2 storage account on the subscription where the FOCUS export will land. Use these values:

    * **Subscription:** the subscription that hosts (or will host) the FOCUS export
    * **Resource group:** create new — `nops-focus-exports-rg`
    * **Storage account name:** `nopsfocus<tenant-prefix>` (the wizard displays the exact name to copy)
    * **Region:** any region (East US recommended for the lowest export latency)
    * **Performance:** Standard
    * **Redundancy:** Locally-redundant storage (LRS)

    After the account is created, open it and go to **Data storage → Containers**, then click **+ Container** and create a private blob container called `focus-exports`.
  </Step>

  <Step title="Create the FOCUS export">
    In **Cost Management > Exports**, create a new export:

    * **Template:** Cost and usage (FOCUS)
    * **Format:** Parquet, **Compression:** Snappy
    * **Recurrence:** Daily
    * **Storage account:** the one you just created
    * **Container:** `focus-exports`, **Directory:** `nops-clara`
    * **Scope (EA):** Billing Account (enrollment)
    * **Scope (MCA):** Billing **Profile** (not the billing account — MCA cost data lives at the profile level)
    * **Backfill:** after creating the export, click **Export selected dates** and run for the previous **3 months**

    <Warning>
      **MCA with multiple billing profiles:** If you have multiple billing profiles with spend, repeat this step for each one and point every export at the same storage account. An export at billing-account scope will write valid Parquet files with zero data rows for MCA.
    </Warning>
  </Step>

  <Step title="Assign Storage Blob Data Reader on the storage account">
    Navigate to the storage account you created **> Access control (IAM) > Add role assignment**. Select **Storage Blob Data Reader**, then assign it to the **nOps** enterprise application.
  </Step>
</Steps>

### Network access

If your organization enforces a storage firewall, allowlist the nOps egress IPs (`44.246.45.84`, `52.11.53.164`, `52.39.155.200`) on the storage account and enable **"Allow Azure services on the trusted services list"**. Otherwise, leave network access set to **Enabled from all networks** so the FOCUS export writer and the nOps reader can reach the account.

### When to use this flow

* ESO-style least-privilege security reviews
* Customers who reject Cost Management Contributor or Owner-on-storage
* Tenants where Global Admin "Access management for Azure resources" elevation is not permitted

The minimum-permissions integration can be upgraded to Commitment Management later — at that point additional write-level roles are required and documented separately.

***

## 5. Verify Connection

After completing the setup, verify that all permissions are correctly configured.

<Steps>
  <Step title="Wait for Role Propagation">
    After completing the setup, the nOps wizard displays a cooldown timer. Azure role assignments typically take 2-5 minutes to propagate. Wait for the timer to complete before verifying.

    <Info>
      The cooldown is shorter (1 minute) for Azure Portal manual setup since you spent time completing the steps. For the Terraform method, it's 3 minutes since the configuration applies quickly.
    </Info>
  </Step>

  <Step title="Click Verify Permissions">
    Click the **Verify Permissions** button. nOps checks:

    * **Subscription Access** -- Can the service principal list and access subscriptions under the management group?
    * **Cost Management Access** -- Can the service principal query cost data?
    * **Individual Role Checks** -- Is each required role (Reader, Cost Management Contributor, Monitoring Reader, Owner) properly assigned?
  </Step>

  <Step title="Review Results">
    The wizard displays per-role validation results with pass/fail indicators. If all checks pass, you will see a **"Script Executed!"** confirmation with the number of connected subscriptions.
  </Step>
</Steps>

<Warning>
  **Verification Failed?** -- If some permissions are not yet detected:

  1. Wait a few more minutes for Azure role propagation
  2. Ensure the setup completed without errors (check Terraform output or verify all Portal steps were completed)
  3. Verify your account has sufficient permissions (Owner or User Access Administrator at the management group scope)
  4. Click **Verify Permissions** to re-check
</Warning>

***

## Next Steps

Once the Savings Analysis integration is verified, nOps begins ingesting your cost data and generating optimization recommendations.

To enable automated purchasing and management of Azure Reservations and Savings Plans, you can upgrade your integration to the Commitment Management tier:

<Card title="Azure Commitment Management Setup" icon="chart-line" href="/azure/commitment-management-setup">
  Configure nOps to manage Azure Reservations and Savings Plans on your behalf, including automated purchasing and optimization.
</Card>
