Skip to content

Azure Optimizations

The Azure Optimizations module scans your Azure subscription for cost reduction opportunities using Azure Cost Management and Azure Advisor APIs. It stores findings as "opportunities" in the shared database and surfaces them in the Strawly UI.

What it scans

  • Rightsizing recommendations — virtual machines and services that are over-provisioned relative to actual usage
  • Termination opportunities — idle or unused resources that can be stopped or deleted
  • Azure Advisor cost recommendations

Results appear in the Azure Optimizations section of the Strawly UI with estimated savings, severity, and status tracking.

Prerequisites

Before enabling this module, you need an Azure Service Principal with read-only access to your subscription.

Create a Service Principal

az ad sp create-for-rbac --name strawly-readonly --role Reader --scopes /subscriptions/<subscription-id>

This outputs a JSON object with appId (client ID), password (client secret), and tenant (tenant ID). Save these values.

Assign Cost Management Reader

The Reader role alone does not grant access to cost data. Also assign Cost Management Reader:

az role assignment create \
  --assignee <app-id> \
  --role "Cost Management Reader" \
  --scope /subscriptions/<subscription-id>

Find your subscription ID

az account show --query id --output tsv

Enable the module

  1. Open strawly-deployment.yml and set enabled: true:

    yaml modules: optimizations-azure: enabled: true port: 3002

  2. Add credentials to .env:

    bash AZURE_TENANT_ID=<your-tenant-id> AZURE_CLIENT_ID=<your-client-id> # the "appId" from the SP creation output AZURE_CLIENT_SECRET=<your-client-secret> # the "password" from the SP creation output AZURE_SUBSCRIPTION_ID=<your-subscription-id>

  3. Regenerate and redeploy:

    bash npm run generate-compose docker compose -f docker-compose.generated.yml pull docker compose -f docker-compose.generated.yml up -d

  4. Verify the module is running:

    bash curl http://localhost:3002/health

    Expected response:

    json { "status": "ok", "service": "strawly-optimizations-azure", "timestamp": "2026-05-17T03:00:00.000Z" }

Scan schedule

By default, the module scans your Azure subscription daily at 3:00 AM UTC. To change the schedule, edit strawly-deployment.yml:

modules:
  optimizations-azure:
    schedule:
      cron: "0 6 * * *"   # 6 AM UTC
      timezone: "UTC"

Then regenerate and redeploy.

Trigger a manual scan

To run a scan immediately without waiting for the schedule:

curl -X POST http://localhost:3002/opportunities/scan

Or use the Scan now button in the Azure Optimizations section of the Strawly UI (via Settings).

The module also reads its enabled state and schedule from the shared settings table. You can toggle the module on/off and change the schedule from the Strawly Settings UI without redeploying.

API endpoints

The module exposes these endpoints (consumed by the Strawly backend — not intended for direct use):

Method Path Description
GET /health Liveness check
GET /opportunities List opportunities. Filter by ?status, ?category, ?priority
GET /opportunities/:id Get a single opportunity
POST /opportunities/scan Trigger an immediate scan
PATCH /opportunities/:id/status Update status (identified / in-progress / implemented / dismissed)

Working with opportunities

Opportunities appear in the Azure Optimizations dashboard in the Strawly UI. For each opportunity you can:

  • View the estimated monthly saving and affected resource
  • Update the status as your team acts on it (In Progress, Implemented, Dismissed)
  • Filter by category, status, and severity

Troubleshooting

No opportunities appearing after the first scan

  • Confirm the scan has run: check the module logs for Azure scan complete.
  • If the scan ran but found zero opportunities, Azure Advisor may not have generated recommendations yet for your subscription — this is normal for new or small subscriptions.
  • Trigger a manual scan and watch the logs: docker compose -f docker-compose.generated.yml logs -f optimizations-azure

Authentication errors

Symptoms: logs show 401 Unauthorized or 403 Forbidden from Azure APIs.

  • Verify AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET are correct in .env.
  • Confirm the Service Principal has both Reader and Cost Management Reader roles on the subscription.
  • Check the client secret has not expired in Azure Active Directory.

Module exits immediately on startup

  • Check logs: docker compose -f docker-compose.generated.yml logs optimizations-azure
  • Missing environment variables are the most common cause. Confirm all four AZURE_* variables are set in .env.