Setup guide
Setting up CostWarden
Six steps, about fifteen minutes, most of it waiting on AWS. Follow it top to bottom — each step says what you should see afterwards, so you can tell a slow step from a stuck one.
Before you start
- An AWS account with Cost Explorer enabled. If it has never been switched on, AWS takes about 24 hours to populate data after you do.
- Permission to create an IAM role in that account — or someone who has it, since this is the one step you may need to delegate.
- Admin access to wherever alerts should land (Slack, Teams, Google Chat or PagerDuty).
What you are actually agreeing to
One direction out, one direction back, and no key anywhere in the picture.
We ask AWS to let us assume a role inside your account, proving an external ID only your organization has. AWS hands back temporary credentials that expire on their own, and we read cost data with them. Nothing travels the other way, and nothing we hold is a secret you would have to rotate.
Create your account
1 minute
Sign up with your work email and a password of at least 8 characters. You'll get a confirmation email — click the link before doing anything else.
Confirming matters more than it looks. An unconfirmed address can't see or accept invitations to other organizations, because an unconfirmed address isn't proof you own it. If a colleague invited you and you can't find the invite, this is almost always why.
Signing up creates a personal organization for you automatically. Everything below happens inside it, and you can rename it or create others later from Settings.
Connect your AWS account
5 minutes
This is the only step with any real work in it. You create a read-only IAM role in your own AWS account and paste its ARN. CostWarden never asks for an access key.
Go to Settings → Cloud connection and press Start connecting. That generates an external ID unique to your organization and reveals the walkthrough. Then, in AWS:
- Open IAM → Roles → Create role and choose Custom trust policy as the trusted entity type.
- Paste the trust policy shown in Settings. It names our AWS account and requires your external ID. Copy it with the button rather than retyping — the external ID must match exactly.
- Add permissions. Choose Create inline policy (not Use existing policy), open the JSON tab, and paste the permissions policy from Settings. Name it
CostWardenReadOnlyAccess— that name is cosmetic. AWS should summarise it as three services: Cost Explorer (Read), Compute Optimizer (List) and EC2 (List). - Name the role
CostWardenReadOnly— this one is not cosmetic. Our permission to assume it is scoped to that exact name, so a misspelling fails later with an opaque AccessDenied. - Copy the role's ARN from its summary page, paste it back into Settings, and press Verify & connect.
Here are both documents, so you can read them now. Settings renders these same two filled in — this account id and this external ID are placeholders, because you do not have an organization yet. Copy from Settings when you get there rather than from here.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<COSTWARDEN_ACCOUNT_ID>:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "<YOUR_EXTERNAL_ID>"
}
}
}
]
}{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ce:GetCostAndUsage",
"compute-optimizer:GetEC2InstanceRecommendations",
"compute-optimizer:GetEBSVolumeRecommendations",
"ec2:DescribeInstances",
"ec2:DescribeVolumes"
],
"Resource": "*"
}
]
}What you should see: the save performs a real assume-role against your trust policy before storing anything, so a green “Connected” means it genuinely works — not that we wrote down a string. If it fails, the error tells you which of the two likely causes it is.
Run the first sync — and read it honestly
2 minutes
Press Sync now. We pull up to 90 days of Cost Explorer history and scan all of it, so the dashboard and the anomaly history are populated from the first run rather than filling in a day at a time.
Three outcomes are all normal, and Settings tells you which you got:
- Cost points written. Working as intended.
- “Not enough cost history yet”. AWS has no full settled day of spend for this account. Expected right after enabling Cost Explorer, and for an account with little usage. Nothing you do makes it arrive sooner.
- An error. Settings shows the message verbatim and degrades the connection status — it will not keep saying “Connected” while syncs fail.
Two things to expect. Cost Explorer publishes a day's spend 24–48 hours late, so the freshest day anyone can analyse is already a day or two old. And the detector needs four weeks of same-weekday history before it will judge a day — under that it stays quiet rather than guessing. A brand-new account is not broken, it is accumulating.
From here it runs itself: a scan every day at 13:00 UTC. Sync now is for when you don't want to wait.
Connect an alert channel
3 minutes
Until you connect one, alerts have nowhere to go — the product still detects, but you'd have to open the dashboard to find out, which is the thing it exists to avoid. Go to Alerts and pick a type; the instructions change to match.
- Slack
- Create an app from scratch, turn on Incoming Webhooks, add one to the channel alerts should land in, and paste the URL.
- Microsoft Teams
- In the channel, ⋯ → Workflows → the template “Post to a channel when a webhook request is received”. Teams retired the old Office 365 connectors, so Workflows is the current way in; a legacy connector URL still works if you already have one.
- Google Chat
- Open the space, click its name → Apps & integrations → Webhooks → Add webhooks. Only works in spaces, not direct messages.
- PagerDuty
- On the service that should be paged: Integrations → Add integration → Events API V2, then copy the Integration Key.
We deliver a test message before saving for the three webhook types, so a wrong URL fails there rather than silently swallowing your first real alert. PagerDuty is the exception: its only message is a triggered incident, and testing a form field shouldn't page whoever is on call. Its key is checked by shape instead.
Connect as many as you like — every enabled channel receives every alert. Pausing a channel keeps it connected and sends nothing.
Invite your team
1 minute
Members share one AWS connection, one dashboard and one set of channels. There are three roles:
| Role | Can do |
|---|---|
| Owner | Everything, including billing, members and renaming the organization. |
| Admin | Operate the product: connect and disconnect the AWS account and alert channels. No billing, no member management. |
| Member | Use the product: dashboards, anomaly history, triage, sync. |
We don't send invitation emails yet. You enter an address; the invite is waiting in that person's own Settings once they sign up with it and confirm it. Tell them out-of-band. Invites expire after 14 days — Settings shows the date and badges expired ones so you're not waiting on someone who can no longer get in.
Understand the trial and billing
1 minute
Your 14-day trial starts when your AWS connection first works — not when you sign up. Waiting on someone to approve an IAM role shouldn't eat your evaluation, and no card is needed to start. Owners see the days remaining in Settings → Billing.
When it ends without a subscription, we stop collecting new data — and that is all. The daily scan skips your organization and no new alerts go out, but everything already synced stays exactly where it is and stays readable, with a banner saying why it stopped moving. Nothing is deleted. Subscribing picks it back up on the next run.
A failed payment keeps working for seven days while the card is retried, so an expired card doesn't cost you a week of alerts. If you cancel, access runs to the end of the period you already paid for. Checkout and invoicing are handled by Lemon Squeezy as Merchant of Record — card details never reach CostWarden.
When something looks wrong
Most of these are the product working correctly and saying so badly. Each row says which.
- “Verify & connect” fails with AccessDenied
- AWS returns the same error whether the trust policy is wrong or the external ID doesn't match, so it won't tell you which. Re-copy the trust policy from Settings rather than reading it — a single character in the external ID is enough.
- Connected, but the dashboard is empty
- Check Settings → Cloud connection. It reports the last sync: “no usable cost data” means AWS has nothing settled yet, an error shows the message. If the last sync succeeded, the account genuinely has no spend in the window.
- No anomalies after a week
- Often correct — it means nothing broke from its baseline. But the detector also needs four weeks of same-weekday history before it will judge a day, so a young account is quiet by design rather than by verdict.
- A channel shows a red “failing” badge
- The webhook rejected our last delivery and the error is shown verbatim. Usual causes: the Slack app was revoked, or the channel was deleted. Fix it upstream, then press Test — a successful test clears the badge.
- An invited colleague can't find their invitation
- Almost always an unconfirmed email address. Invitations only appear for a confirmed address. Check the Account card in their Settings — it says which.
- “Syncing is paused” on the dashboard
- The trial ended, or a subscription lapsed. Existing data is intact and readable; an owner can restart collection from Settings → Billing.
That’s the whole setup
After this it runs on its own and you should mostly forget it exists — until the day it tells you something.