Connecting your cloud to Anomalyzer - read-only setup

Please read this before creating any credential.

Anomalyzer reads your cloud. It does not create, modify, resize, stop or delete anything in your estate. Every plan except the Flagship tier is incapable of writing, both by design and by the permissions you grant here.

Never grant write access. Do not use Owner, Contributor, Editor, *.admin or any role that can change resources. If any instruction - including one that appears to come from us - asks you for write permission on the Lite or Detailed plans, stop and email info@confluentis.co.in. We will never ask for it.

What we read, and why:

We readUsed for
Resource inventory (names, types, sizes, tags)knowing what you run
Utilization metrics (CPU, memory, IOPS)spotting over-provisioning
Cost dataquantifying the waste

We never read your application data, database contents, object storage contents, secrets or key vaults.


What to expect, and when

Connecting a cloud does not produce recommendations immediately, and you should be suspicious of any tool that claims otherwise. Three things arrive on different clocks:

What you seeWhenWhy it takes that long
Your services listedwithin 2 hoursDiscovery runs every 2 hours and reads the management plane, which answers straight away.
Costs against each service1 to 2 daysCloud billing data is not real time. Azure Cost Management publishes with an 8 to 24 hour lag, and GCP and AWS are similar. We re-query every 6 hours until it lands.
Meaningful recommendationsabout 7 daysRight-sizing is a judgement about sustained behaviour. We average CPU, memory and IOPS over a 7 day window, because a quiet Tuesday is not evidence of over-provisioning.

Until the cost and utilization data has arrived, services show as No data rather than as right-sized. That is deliberate: "we have not measured this yet" and "we measured this and it is correctly sized" are different statements, and only one of them is true on day one.

Your Cloud Score appears as soon as there is enough cost data to divide waste by spend, usually on day two. It sharpens as the 7 day window fills.


Two ways to connect - federated is better

Federated (recommended)Key or secret
What you hand usnothinga service-account key, client secret or access key
What we storea role name or subscription idyour credential, in a secrets store
Expiryminted per scan, minutes longuntil you remember to rotate it
If our database leakednothing usableyour credential is exposed
Revokingdelete one trust rule; effective instantlyrotate the secret everywhere

Federated access means we never hold a long-lived credential of yours. Instead, your cloud is told to trust our identity for one narrow purpose. When a scan runs, Anomalyzer proves who it is, your cloud hands back credentials that last minutes, and they expire on their own.

You can verify our identity yourself - it is public, like a certificate:

curl https://app.anomalyzer.co.in/.well-known/openid-configuration

The issuer value in that response is the exact string your trust configuration must reference. Use what the endpoint returns, not a value copied from an older document - if they disagree, your cloud will reject our token and the connection test will fail.

Whichever cloud you use, the trust rule is scoped to your organization alone. Our token carries a subject of the form anomalyzer:org:<your-org>:<cloud> and your trust configuration matches it exactly, so a token minted for one customer cannot be used against another.

In the app: open Cloud connections → Connect a cloud, pick your cloud, and leave Connection method on Federated - no secret. The screen shows these commands with your own ids already filled in, and a Copy button. Run them, paste back the one value asked for, then use Test connection - it must pass before Connect read-only will save.


Microsoft Azure

Least work of the three. No app registration, no client secret, nothing to create except role assignments. You paste back two ids you already know.

Before you start

Federated - recommended

--assignee is the object id of Anomalyzer's service principal. It is the same for every customer and is not a secret.

Linux and macOS (bash, zsh)

SUB="<your-subscription-id>"
ANOMALYZER_SP="5daae419-c4a9-403f-838d-0825e543b8ce"

# Spend - lets us quantify the waste
az role assignment create \
  --assignee "$ANOMALYZER_SP" \
  --role "Cost Management Reader" \
  --scope "/subscriptions/$SUB"

# Inventory and utilization - lets us name the services behind the number.
# Without this you get a total with nothing attributed to it.
az role assignment create \
  --assignee "$ANOMALYZER_SP" \
  --role "Reader" \
  --scope "/subscriptions/$SUB"

Windows (PowerShell)

$Sub = "<your-subscription-id>"
$AnomalyzerSp = "5daae419-c4a9-403f-838d-0825e543b8ce"

# Spend - lets us quantify the waste
az role assignment create `
  --assignee $AnomalyzerSp `
  --role "Cost Management Reader" `
  --scope "/subscriptions/$Sub"

# Inventory and utilization - lets us name the services behind the number.
# Without this you get a total with nothing attributed to it.
az role assignment create `
  --assignee $AnomalyzerSp `
  --role "Reader" `
  --scope "/subscriptions/$Sub"

Then paste your tenant id and subscription id into Anomalyzer. Find them with az account show --query "{tenant:tenantId, subscription:id}".

Key-based - if federation is not an option

Create a service principal with Reader only, then add the two data roles:

Linux and macOS

SUB="<your-subscription-id>"

az ad sp create-for-rbac \
  --name "anomalyzer-reader" \
  --role "Reader" \
  --scopes "/subscriptions/$SUB"

# Cost Management Reader -> spend;  Monitoring Reader -> utilization
APP_ID="<appId from the output above>"
az role assignment create --assignee "$APP_ID" \
  --role "Cost Management Reader" --scope "/subscriptions/$SUB"
az role assignment create --assignee "$APP_ID" \
  --role "Monitoring Reader" --scope "/subscriptions/$SUB"

Windows (PowerShell)

$Sub = "<your-subscription-id>"

az ad sp create-for-rbac `
  --name "anomalyzer-reader" `
  --role "Reader" `
  --scopes "/subscriptions/$Sub"

$AppId = "<appId from the output above>"
az role assignment create --assignee $AppId `
  --role "Cost Management Reader" --scope "/subscriptions/$Sub"
az role assignment create --assignee $AppId `
  --role "Monitoring Reader" --scope "/subscriptions/$Sub"

create-for-rbac prints appId, password and tenant once. Copy them straight into Anomalyzer; the password cannot be retrieved later, only reset.

Removing Azure access

az role assignment delete --assignee "5daae419-c4a9-403f-838d-0825e543b8ce" \
  --scope "/subscriptions/<your-subscription-id>"

For the key-based route, az ad sp delete --id <APP_ID> removes it entirely.


Google Cloud

Before you start

Federated - recommended

This is Google's direct workload identity federation: no service account is created, so there is no key to hold, rotate or leak.

Linux and macOS (bash, zsh)

PROJECT_ID="<your-project-id>"
PROJECT_NUMBER="$(gcloud projects describe "$PROJECT_ID" --format='value(projectNumber)')"
ORG_SLUG="<your-anomalyzer-org-slug>"     # shown on the Connect a cloud screen
POOL="anomalyzer-pool-$ORG_SLUG"

gcloud iam workload-identity-pools create "$POOL" \
  --project="$PROJECT_ID" --location="global" --display-name="Anomalyzer reader"

gcloud iam workload-identity-pools providers create-oidc anomalyzer-oidc \
  --project="$PROJECT_ID" --location="global" --workload-identity-pool="$POOL" \
  --issuer-uri="https://app.anomalyzer.co.in" \
  --attribute-mapping="google.subject=assertion.sub" \
  --allowed-audiences="//iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$POOL/providers/anomalyzer-oidc"

MEMBER="principalSet://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$POOL/*"

# Inventory and utilization
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
  --role="roles/viewer" --condition=None --member="$MEMBER"

# Spend. On the BILLING ACCOUNT, not the project.
BILLING_ACCOUNT="$(gcloud billing projects describe "$PROJECT_ID" \
  --format='value(billingAccountName)')"

gcloud billing accounts add-iam-policy-binding "${BILLING_ACCOUNT#billingAccounts/}" \
  --role="roles/billing.viewer" --member="$MEMBER"

Windows (PowerShell)

Note the last step: ${VAR#prefix} is a bash-only expansion that does nothing in PowerShell, so the billing id would keep its billingAccounts/ prefix and the binding would be rejected. Use -replace instead.

$ProjectId = "<your-project-id>"
$ProjectNumber = (gcloud projects describe $ProjectId --format="value(projectNumber)")
$OrgSlug = "<your-anomalyzer-org-slug>"
$Pool = "anomalyzer-pool-$OrgSlug"

gcloud iam workload-identity-pools create $Pool `
  --project=$ProjectId --location="global" --display-name="Anomalyzer reader"

gcloud iam workload-identity-pools providers create-oidc anomalyzer-oidc `
  --project=$ProjectId --location="global" --workload-identity-pool=$Pool `
  --issuer-uri="https://app.anomalyzer.co.in" `
  --attribute-mapping="google.subject=assertion.sub" `
  --allowed-audiences="//iam.googleapis.com/projects/$ProjectNumber/locations/global/workloadIdentityPools/$Pool/providers/anomalyzer-oidc"

$Member = "principalSet://iam.googleapis.com/projects/$ProjectNumber/locations/global/workloadIdentityPools/$Pool/*"

gcloud projects add-iam-policy-binding $ProjectId `
  --role="roles/viewer" --condition=None --member=$Member

$Billing = (gcloud billing projects describe $ProjectId `
  --format="value(billingAccountName)") -replace '^billingAccounts/', ''

gcloud billing accounts add-iam-policy-binding $Billing `
  --role="roles/billing.viewer" --member=$Member

Then paste your project id and project number into Anomalyzer.

Key-based - if federation is not an option

This route creates a JSON key file. Treat it like a password: anyone holding it has the access you granted, until you delete the key.

Linux and macOS

PROJECT_ID="<your-project-id>"
SA="anomalyzer-reader"

gcloud iam service-accounts create "$SA" \
  --project="$PROJECT_ID" --display-name="Anomalyzer read-only"

EMAIL="$SA@$PROJECT_ID.iam.gserviceaccount.com"

# viewer            -> see resources
# monitoring.viewer -> utilization metrics
# cloudasset.viewer -> fast inventory
for ROLE in roles/viewer roles/monitoring.viewer roles/cloudasset.viewer; do
  gcloud projects add-iam-policy-binding "$PROJECT_ID" \
    --member="serviceAccount:$EMAIL" --role="$ROLE" --condition=None
done

# Downloads the key to a file in the CURRENT directory.
gcloud iam service-accounts keys create anomalyzer-key.json \
  --iam-account="$EMAIL"

Windows (PowerShell)

$ProjectId = "<your-project-id>"
$Sa = "anomalyzer-reader"

gcloud iam service-accounts create $Sa `
  --project=$ProjectId --display-name="Anomalyzer read-only"

$Email = "$Sa@$ProjectId.iam.gserviceaccount.com"

foreach ($Role in @("roles/viewer","roles/monitoring.viewer","roles/cloudasset.viewer")) {
  gcloud projects add-iam-policy-binding $ProjectId `
    --member="serviceAccount:$Email" --role=$Role --condition=None
}

gcloud iam service-accounts keys create anomalyzer-key.json `
  --iam-account=$Email

What to do with anomalyzer-key.json:

  1. It is written to the folder you ran the command in. Find it with ls anomalyzer-key.json (or dir anomalyzer-key.json on Windows).
  2. Open it in a text editor - Notepad, VS Code, cat, anything. It is a single JSON object starting { "type": "service_account", ....
  3. Select all of it and copy - the whole file including the outer braces, not just the private_key line.
  4. Paste it into the Service account key (JSON) box in Anomalyzer and save.
  5. Delete the file from your machine. Anomalyzer stores it encrypted; a copy sitting in your Downloads folder is the part that gets leaked.

If you prefer the console: IAM & Admin → Service Accounts → your account → Keys → Add key → Create new key → JSON. The browser downloads it; the same five steps apply.

Removing Google Cloud access

# Federated
gcloud iam workload-identity-pools delete "$POOL" --location=global --project="$PROJECT_ID"

# Key-based
gcloud iam service-accounts delete "$EMAIL" --project="$PROJECT_ID"

Amazon Web Services

Before you start

Federated - recommended

We never receive an access key or a secret key. Your account grants a narrow, read-only trust directly to our identity using AWS's own AssumeRoleWithWebIdentity. You can revoke it from your console at any time, with nothing to coordinate with us.

What gets created, all inside your own account:

  1. An IAM OIDC identity provider trusting https://app.anomalyzer.co.in.
  2. An IAM role whose trust policy accepts only tokens carrying your organization's exact subject (anomalyzer:org:<your-slug>:aws). The OIDC provider is shared infrastructure across our customers; the role condition is what makes it yours, so no other customer's token can assume it.
  3. A read-only managed policy attached to that role. It is a specific list of Describe, List, Get and Cost Explorer actions, not the AWS-managed ReadOnlyAccess policy. Nothing in it can create, modify or delete.

One CloudFormation template creates all three together.

Step 1 - download the template. On Cloud connections → Connect a cloud → AWS, set the method to Federated - no secret (recommended), then click Download CloudFormation template. Always download fresh rather than reusing an older file; the permission list changes as we add support for more services.

Step 2 - create the stack. Read this step carefully. In the AWS console go to CloudFormation → Stacks, click the small arrow next to Create stack (not the button itself) and choose "With new resources (standard)".

The other option, "With existing resources (import resources)", is a different operation entirely: it attaches resources that already exist. If the wizard starts asking you for "identifier values" for a role that does not exist yet, you are in the wrong one. Cancel and start again.

Then:

  1. Prepare template: leave "Choose an existing template".
  2. Specify template: choose Upload a template file and select the file you downloaded.
  3. Stack details: any stack name (for example anomalyzer-reader). Leave Audience at its default sts.amazonaws.com. For OrgSlug, enter the exact value shown in the Anomalyzer drawer.
  4. Accept the defaults on "Configure stack options".
  5. On Review, tick the acknowledgement that IAM resources will be created.
  6. Submit, and wait for CREATE_COMPLETE.

Step 3 - connect. Open the stack's Outputs tab, copy the value labelled RoleArn, paste it into the Anomalyzer drawer and press Test connection. That makes one live read-only call (sts:GetCallerIdentity after assuming the role) before anything is saved. Then press Connect read-only.

Cost data needs one more thing, in AWS

Inventory works as soon as the role exists. Cost does not, and the reason is not obvious: we read per-resource cost through Cost Explorer's GetCostAndUsageWithResources, and that call needs resource-level data enabled in the payer account. That is a preference in your own billing console, unrelated to the CloudFormation stack. Without it the call succeeds and returns nothing, so the estate shows every service correctly with $0 against all of them.

It is two separate toggles, and this is the part that catches people out. Turning on the first without the second leaves EC2 and EBS at zero no matter how long you wait.

  1. Go to Billing and Cost Management → Cost Management Preferences. Depending on console version this may appear as Cost Explorer → Preferences; searching the console bar for "Cost Management Preferences" gets you there directly.
  2. Open the Cost Explorer tab on that page.
  3. Under Granular data → Daily granularity, confirm Resource-level data at daily granularity is ticked, with All services selected. This covers most services, including S3.
  4. Under Hourly granularity, separately tick EC2-Instances (Elastic Compute Cloud - Compute) resource-level data. This is a genuinely different toggle from the one above. EC2, and EBS which bills under the same service category, will not report resource-level cost without it.
  5. Save preferences.

This takes effect going forward, not retroactively, and can take up to a day to appear. AWS charges a small fee for hourly resource-level records, in the region of $0.01 per 1,000 records, which for a typical account is a fraction of a dollar a month.

If you cannot find the page at all, or get an access-denied message on it despite holding admin IAM permissions, a separate root-account setting called IAM User and Role Access to Billing Information may be switched off. Only the account's actual root user can turn that on.

When we widen the permissions

Adding support for a new AWS service means new read actions on the policy. Your role and its ARN do not change, so nothing is re-entered on our side:

  1. Download the template again from the drawer.
  2. CloudFormation → Stacks → your stack → Update stack (not Create).
  3. Replace current template → Upload a template file, select the new file.
  4. Keep the same OrgSlug. Continue to UPDATE_COMPLETE.

If the update reports "The submitted information didn't contain changes", your browser served a cached copy of the older template. Download it again in a private window and check the file actually differs before re-uploading.

How we find your resources

Discovery works in three layers, each catching what the one before it cannot:

  1. Purpose-built collectors for EC2, EBS, RDS, S3, EKS, App Runner and SageMaker. These carry the most detail: instance type, engine version, storage class and so on.
  2. A tagging sweep across every other service, using the AWS Resource Groups Tagging API. It needs no setup from you, but it can only see resources carrying at least one tag.
  3. An optional AWS Config sweep, described next.

The first two run automatically and cover the large majority of what most accounts hold.

Optional: AWS Config, for untagged resources

The tagging sweep cannot see an untagged resource. If your account has meaningful untagged infrastructure, which is common for older accounts or anything created before a tagging policy existed, enabling AWS Config closes that gap: Config records every resource type it supports regardless of tags.

This is your decision, and it is off by default. We never enable it on your behalf. Config is a separate AWS service with its own billing, in the region of $0.003 per configuration item plus a small monthly recorder fee. For a typical account that is a few dollars a month or less, but it is a real, ongoing AWS charge for as long as it stays on, independent of anything we charge.

If you want it: Config → set recording strategy to All resource types with customizable overrides, choose Continuous recording, leave AWS's default exclusion of IAM resource types in place (we do not need Config to track IAM), create the Config service-linked role, let it create a bucket for delivery, leave the SNS topic blank, and skip Config Rules. Repeat per region if your estate spans several.

Allow up to an hour for the first inventory of pre-existing resources. Config's initial baseline is slower than its ongoing recording, so an empty Resource Inventory shortly after enabling is normal rather than a fault.

To turn it off later: Config → Settings → Turn off recording. Deleting our CloudFormation stack does not affect Config, because Config is yours and separately managed.

What the role can actually do

Every action in the policy is read-only. The whole list, by verb:

VerbCountWhat it means
Get*8Retrieve one existing value: cost data, bucket location, function or endpoint configuration.
List*9Enumerate existing things: functions, clusters, endpoints, tags, buckets.
Describe*12Retrieve detail about existing things: instances, volumes, snapshots, DB instances, clusters, nodegroups, services, endpoints, Config recorder status.
Select*1A SQL-style query against data AWS Config has already recorded. Read-only by definition, the same category as a database SELECT.

There are zero occurrences of Create, Put, Delete, Update, Modify, Attach, Detach, Start, Stop, Terminate, Reboot, Authorize, or any other state-changing verb anywhere in it.

Two things worth understanding if you are reviewing this properly. First, the policy's Resource: "*" scope does not escalate anything: it controls which resources these already-read-only actions apply to, not what may be done to them. A read-only action list applied broadly is still read-only. Second, the sts:AssumeRoleWithWebIdentity statement in the trust policy is a different kind of statement altogether. It governs who may assume the role at all, scoped to tokens minted for your organization only, rather than granting any permission over your resources. Once assumed, everything that identity can do is bounded by the read-only list above.

The full policy is visible in the CloudFormation template before you ever run it, and we would rather you read it there than take this summary as the last word.

Key-based - if federation is not an option

Linux and macOS

aws iam create-user --user-name anomalyzer-reader

aws iam attach-user-policy --user-name anomalyzer-reader \
  --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess

# Cost Explorer is a separate grant from ReadOnlyAccess
aws iam attach-user-policy --user-name anomalyzer-reader \
  --policy-arn arn:aws:iam::aws:policy/job-function/Billing

aws iam create-access-key --user-name anomalyzer-reader

Windows (PowerShell)

aws iam create-user --user-name anomalyzer-reader

aws iam attach-user-policy --user-name anomalyzer-reader `
  --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess

aws iam attach-user-policy --user-name anomalyzer-reader `
  --policy-arn arn:aws:iam::aws:policy/job-function/Billing

aws iam create-access-key --user-name anomalyzer-reader

create-access-key prints AccessKeyId and SecretAccessKey once. Copy both into Anomalyzer immediately; the secret cannot be retrieved again.

Removing AWS access

Delete the CloudFormation stack. That removes the role, the OIDC provider and the managed policy together, in one action: CloudFormation → Stacks → your stack → Delete. Nothing is needed on our side; the connection simply stops working the next time it is attempted.

For the key-based route, detach its policies and then aws iam delete-user --user-name anomalyzer-reader.

If AWS does not look right

What you seeWhyWhat to do
The wizard asks for "identifier values" for a role that does not existYou picked "With existing resources (import resources)"Cancel, and start again with With new resources (standard)
Test connection fails right after the stack completesAWS can take a moment to propagate a new IAM roleWait a minute or two and retry, changing nothing
Test connection fails mentioning the token issuerNot something to diagnose from the AWS sideSend us the exact error at info@confluentis.co.in
Services appear, but every one shows $0Cost Explorer resource-level data is not enabled, or the role predates our current policyEnable resource-level data in the payer account, then update the stack as above
An update says "didn't contain changes"Your browser served a cached copy of the templateDownload again in a private window and confirm the file differs
Connected, but no services at allUsually a filter on the Services page, most often EnvironmentClear the filters first; if it persists, update the stack

A few more symptoms worth knowing, with what each one actually means:

SymptomCauseFix
The Create stack wizard asks for "identifier values" for AnomalyzerReaderRoleYou picked "With existing resources (import resources)" instead of "With new resources (standard)"Cancel, go back to Stacks, and start again from the correct dropdown option
A stack update says "The submitted information didn't contain changes"Your browser served a cached copy of the template, so the file is identical to what is already deployedDownload again in a private window, confirm the file differs, then re-upload
Test connection fails immediately after the stack completesAWS's own propagation of a brand-new IAM role sometimes takes a minuteWait 1 to 2 minutes and retry without changing anything
Connected, but no resources under ServicesMost often a filter on the Services page, especially Environment, is hiding results that are genuinely there. Failing that, the role may predate support for a service in your accountClear the filters first. If that is not it, update the stack as described above
Resources appear but everything shows $0The resource-level cost preferences above are not both enabled, most commonly the EC2 hourly oneEnable both toggles, then allow up to a day
Cost is right for some resource types and missing for othersDifferent AWS services report cost at different granularity, some by ID and some by name. This is normal AWS behaviour that we already account forIf a specific type still shows nothing after 24 to 48 hours despite real spend, tell us the type so we can look
AWS Config shows 0 resources shortly after you enable itConfig's first baseline scan of pre-existing resources is slower than its recording of new changesAllow up to an hour, and check Config's own Resources page before assuming a fault

Bring your own LLM

Already using OpenAI, Azure OpenAI, Anthropic, Gemini, or another enterprise LLM? Bring your own model and credentials, and we'll help you integrate it securely with your existing authentication and access controls.

Contact info@confluentis.co.in and we'll help you with the authentication integrations.


How to verify we cannot write

Any of these prove it independently of our word:

Removing access

Deleting the service principal, service account or IAM role instantly and permanently cuts our access; nothing else is required. In the app, Cloud connections → Remove deletes the stored credential, and an admin can purge the entire organization and its data from the admin portal.

Pause instead of Remove if you only want scanning to stop: it keeps what we have already collected, so resuming restores your estate rather than starting discovery from nothing.


Questions, or anything in this guide that looks wrong: info@confluentis.co.in

← Back to the registration form