Security
How CostPulse Handles Your AWS Account
Trust is the foundation of our service. You grant us access to cost data in your AWS account — here we transparently explain what exactly happens in your account, which permissions we have, and which we don't.
What CostPulse Deploys in Your AWS Account
When you connect an AWS account, you deploy a CloudFormation stack (CostPulse-Client-Roles-{hash}) in an AWS region of your choice (default: eu-central-1). The stack creates only the following resources:
4 IAM Roles — each with tightly scoped permissions:
| Role | Purpose |
|---|---|
| Cost Reader | Reads your cost data (ce:GetCostAndUsage) — nothing else. |
| Anomaly Detection | Creates and manages cost anomaly monitors. Can only modify/delete resources tagged ManagedBy=costpulse. |
| Budgets Manager | Reads all budgets. Can only create, modify, or delete budgets whose name starts with costpulse-{hash}-. |
| Stack Cleanup | Exclusively used to delete the CostPulse stack during offboarding. Can only remove its own CostPulse resources. |
1 SNS Topic (costpulse-{hash}-alerts) — receives budget alerts and anomaly detections from AWS and forwards them to CostPulse for push notification delivery. Only the AWS services budgets.amazonaws.com and costalerts.amazonaws.com may publish to this topic, restricted to your own account ID.
Nothing else. No Lambda functions, no EC2 instances, no data storage, no networking resources.
{hash} is a deterministic 8-character hex string derived from your ExternalId. It ensures resource name isolation across multiple CostPulse tenants in the same AWS account.
No Access to Credentials
CostPulse never stores long-term AWS credentials (Access Keys or Secret Keys). All access uses temporary AWS STS sessions:
- CostPulse calls
sts:AssumeRole targeting the appropriate role in your account. - Every call requires the correct ExternalId — a UUID generated at onboarding. Without it, access fails even if someone knows our account ID and role ARN (protection against the confused deputy problem).
- AWS STS returns temporary credentials valid for 15 minutes.
- Within a single Lambda execution, credentials are cached in-memory for performance (never persisted to storage). The cache key includes the authenticated Cognito user ID — a warm Lambda can never return another user's cached credentials. Cache entries are proactively evicted when less than 60 seconds of validity remain. Even if a stale entry were still present in the cache, AWS rejects any API call made with expired credentials — they are cryptographically time-bound and become unusable after expiry regardless of caching.
Every STS session uses a descriptive name (e.g., costpulse-cost-explorer, costpulse-anomaly-cleanup) that appears in your CloudTrail logs. You can audit which CostPulse operation triggered each API call at any time.
What CostPulse Cannot Do
- Cannot access any AWS service beyond the 4 IAM roles (no S3, EC2, RDS, or any other service).
- Cannot create or modify IAM users or policies beyond the stack cleanup role's scoped permissions.
- Cannot create budgets outside the
costpulse-{hash}- name prefix. - Cannot delete anomaly monitors or subscriptions unless tagged
ManagedBy=costpulse. - Cannot make any changes to your infrastructure.
Deleting Your Account — What Happens
When you remove an AWS account from CostPulse, offboarding runs automatically:
- Anomaly cleanup: CostPulse deletes the anomaly subscription it created. The anomaly monitor itself (a free, shared resource per AWS account) is not deleted.
- Account marked deleted in the CostPulse database.
- Trust policy neutralized: CostPulse updates the stack cleanup role's trust policy to an unmatchable condition (
sts:ExternalId = "DISABLED"). From this moment, the role is permanently unusable. - Stack deletion: Using the still-valid session, CostPulse calls
cloudformation:DeleteStack. CloudFormation deletes all resources: the 3 operational IAM roles, the SNS topic (including all subscriptions), and the custom resource. - All CostPulse-side data (cost data, account information, SQS queue, database records) is deleted.
What remains: A single orphaned IAM role (costpulse-{hash}-stack-cleanup) with a disabled trust policy. CostPulse cannot assume this role, and it has no usable permissions. You can safely delete it manually from the IAM console.
If automatic deletion fails, CostPulse provides a direct link to the CloudFormation console where you can delete the stack yourself with one click.
Data Transmission and Storage
- All communication between the App, our backend, and AWS is encrypted via TLS.
- Retrieved cost data is temporarily cached in our backend (up to 24 hours) to reduce API calls. Alert records are retained for 90 days.
- Our backend runs on AWS (serverless architecture).
- We never sell or share your data with third parties.
- We don't use your cost data for our own analytics or benchmarks.
Region
During setup, you choose the AWS region where the CloudFormation stack is deployed (default: eu-central-1). The IAM roles created by the stack are global AWS resources and work regardless of the chosen region. The SNS topic for alert forwarding is created in the selected region.
Security Concerns or Questions?
If you discover a security vulnerability or have questions about our security model, contact us at: me@till-it-works.de