Security

Trusted access. No surprise remediations.

FinOps tools fail when teams fear automatic changes. SpendPilot earns trust with read-only scanning by default on AWS, Azure, and GCP—and an approve → fix → verify workflow. Optional approve-then-delete only when you opt in.

Free audit or product connect: set up one cloud (AWS, Azure, or GCP). Jump to setup: AWS · Azure · GCP.

Human-gated changes

Scanning stays read-only by default. Optional approve-then-delete (AWS: unattached EBS, idle EIPs, old snapshots; Azure: unattached disks, unused public IPs; GCP: unattached Persistent Disks, unused static IPs) only runs after an engineer clicks Approve & delete on an opted-in account — never on scan, never without confirmation.

Multiple connect methods

AWS: IAM access keys, temporary session credentials, or a cross-account IAM role (role recommended). Azure: service principal (tenant, client ID, secret) with Reader + Cost Management Reader. GCP: service account JSON key with Viewer + BigQuery Data Viewer on the billing export dataset. Secrets are encrypted at rest.

Least-privilege policies

AWS sample policy covers Cost Explorer and describe/list inventory APIs and denies destructive actions. Azure needs Reader + Cost Management Reader for sync/scan; attach the separate remediation role only if you enable auto-delete. GCP needs project Viewer for inventory and BigQuery Data Viewer for billing export; attach the downloadable SpendPilot remediation role only if you enable Approve & delete.

Region / subscription / project control

On AWS and GCP you choose which regions to waste-scan (country presets available). On Azure scans are subscription-scoped via Resource Graph. Cost sync reflects account spend in your display currency across all three clouds.

Tenant isolation & access control

Org data is scoped by organisation ID. Deactivated users cannot sign in or call APIs. Platform support access is audited when staff enter a customer org.

Global FinOps, local tax where needed

Savings and plans in your display currency (USD, INR, EUR, GBP). India billing profiles support GST invoices; other regions use tax invoices suited to your profile as we expand payments.

Report a vulnerability

If you believe you found a security issue in SpendPilot, email security@spendpilot.dev. Please include steps to reproduce and avoid accessing customer data beyond what is needed to demonstrate the issue. We aim to acknowledge reports within two business days.

Engineer docs: docs/security/threat-model.md, incident-response.md, key-rotation.md, data-map-and-privacy.md, subprocessors.md

Setup guides

Cloud access setup

Pick the cloud you want reviewed for the free audit—or connect in the product later. You only need one.

AWS — connect an account

  1. Prefer a cross-account IAM role (External ID). Access keys or temporary credentials also work.
  2. Deploy the CloudFormation template below (or attach the read-only policy JSON), then paste the Role ARN + External ID under Cloud accounts → AWS after you register.
  3. For a free audit, send us the Role ARN / External ID securely—we never need write access.

Sample read-only IAM policy

Works the same in US, EU, APAC, and India commercial partitions. Attach this policy to a dedicated role or user SpendPilot assumes or keys into. Cost Explorer is global; inventory APIs run in the regions you select to scan.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CostExplorerRead",
      "Effect": "Allow",
      "Action": ["ce:GetCostAndUsage", "ce:GetCostForecast", "ce:GetAnomalies"],
      "Resource": "*"
    },
    {
      "Sid": "InventoryDescribe",
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*",
        "rds:Describe*",
        "elasticloadbalancing:Describe*",
        "elasticache:Describe*",
        "cloudwatch:GetMetricStatistics",
        "compute-optimizer:Get*",
        "s3:ListAllMyBuckets",
        "s3:ListBucketMultipartUploads"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyDestructive",
      "Effect": "Deny",
      "Action": ["ec2:TerminateInstances", "ec2:Delete*", "rds:DeleteDBInstance"],
      "Resource": "*"
    }
  ]
}

Cross-account trust (IAM role)

Prefer a role with ExternalId over long-lived access keys. Replace the platform account id and use the ExternalId SpendPilot shows when you connect.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::SPENDPILOT_ACCOUNT_ID:root" },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": { "sts:ExternalId": "YOUR_ORG_EXTERNAL_ID" }
      }
    }
  ]
}

Downloads

Use these files in the AWS Console. Recommended: deploy the CloudFormation template (creates the role + policy). Use the policy JSON only if you prefer a dedicated IAM user with access keys.

  • CloudFormation role template

    spendpilot-readonly-role.yamlcreates SpendPilotReadOnly with trust + read-only policy

    Download YAML
  • IAM policy JSON

    spendpilot-readonly-policy.jsonattach to an IAM user or role if not using CloudFormation

    Download JSON
  • Optional remediation policy

    spendpilot-remediation-policy.jsonallows approve-then-delete for unattached EBS, idle EIPs, and old snapshots. Attach separately; remove DenyDestructive for those actions. Then enable auto-delete on the AWS account in SpendPilot.

    Download JSON

After the role exists, create an account and paste the Role ARN + External ID under AWS accounts.

Other clouds

Full setup: how it works · free audit.

Azure — connect a subscription

Connect a subscription with a Microsoft Entra app registration(service principal). SpendPilot stays read-only unless you attach the optional remediation role and enable Approve & delete.

  1. Create an App registrationAzure Portal → Microsoft Entra ID → App registrations → New registration. Name it e.g. SpendPilot. Note the Application (client) ID and Directory (tenant) ID.
  2. Create a client secretCertificates & secrets → New client secret. Copy the Value once (it is not shown again).
  3. Assign roles on the subscriptionSubscriptions → your subscription → Access control (IAM) → Add role assignment. Grant the app’s service principal:
    • Reader — inventory / Resource Graph waste scan
    • Cost Management Reader — Cost Management spend sync
  4. Connect in SpendPilotCloud accounts → Azure. Paste Subscription ID, Tenant ID, Application (client) ID, and client secret. Use Validate & connect, then Sync costs and Scan waste.
  5. Optional — Approve & deleteCreate a custom role from the download below (disk + public IP delete only), assign it to the same service principal, then enable “Allow Approve & delete” on the subscription in SpendPilot.

Downloads

  • Optional remediation custom role

    spendpilot-remediation-role.jsonreplace {subscriptionId} in AssignableScopes, then create the custom role in Azure IAM.

    Download JSON

After the app registration exists, create an account and connect under Cloud accounts → Azure.

GCP — connect a project

Connect a GCP project with a service account JSON key. Cost sync needs a Cloud Billing → BigQuery export. Waste scan stays read-only unless you attach the optional remediation role.

  1. Enable APIsIn the target project, enable Cloud Resource Manager, Compute Engine, BigQuery, and (optional) Cloud SQL Admin.
  2. Billing export to BigQueryBilling → Billing export → enable BigQuery export (detailed usage). Note the dataset and table names.
  3. Create a service accountIAM → Service accounts → Create (e.g. spendpilot-readonly). Grant:
    • Project: roles/viewer (or Browser) — connection test + inventory
    • Export dataset: roles/bigquery.dataViewer + roles/bigquery.jobUser — cost sync
  4. Download a JSON keyKeys → Add key → JSON. Store it securely; paste into SpendPilot once (encrypted at rest). Prefer rotating keys regularly.
  5. Connect in SpendPilotCloud accounts → GCP. Paste Project ID, billing export dataset/table, and the SA JSON. Use Validate & connect, set scan regions (defaults follow org country), then Sync costs / Scan waste.
  6. Optional — Approve & deleteCreate a custom role from the YAML below (unattached Persistent Disk + unused static IP delete only), bind it to the same SA, then enable “Allow Approve & delete” on the project in SpendPilot.

Downloads

  • Optional remediation custom role

    spendpilot-remediation-role.yamldisk + static IP delete only

    Download YAML

After the service account exists, create an account and connect under Cloud accounts → GCP.

Ready to connect yourself? Create an account or request a free audit.