AWS STS AssumeRole for SaaS: How SnoozeDB Connects to Your Account Without Storing Keys
When you connect your AWS account to SnoozeDB, we never ask you to copy-paste access keys. No Access Key ID, no Secret Access Key. Yet SnoozeDB is fully able to start and stop your RDS instances on your behalf.
How is that possible? Thanks to a native AWS mechanism called STS AssumeRole. It is the standard used by most SaaS services that interact with their customers' AWS accounts, and it is also what AWS recommends in its own documentation.
This article walks you through what happens under the hood, step by step.
AWS cross-account role for SaaS: two accounts, one secure bridge
The principle is straightforward: instead of sharing permanent credentials between two accounts, one account is authorized to temporarily borrow an identity in the other.
On the SnoozeDB side, there is an IAM user. It is a technical account running on our infrastructure with a single permission: calling the sts:AssumeRole API.
On the client side (your account), there is an IAM role. This role was created automatically when you deployed our CloudFormation template. It defines two things: who is allowed to use it (the "trust policy") and what it permits (permissions limited to four RDS actions).
The AWS STS (Security Token Service) bridges the two. When SnoozeDB needs to act on your instances, it sends a request to STS saying: "I would like to assume this role in this account." STS checks that everything is in order and, if so, returns a set of temporary credentials.
STS AssumeRole tutorial: what happens step by step
Let's take a concrete example. It's 8 PM, and your schedule says your staging database should be stopped.
The SnoozeDB cron triggers. It retrieves from its database your account's Role ARN (something like arn:aws:iam::123456789012:role/SnoozeDBAccessRole) and the associated External ID (a UUID generated during the connection).
With these two pieces of information, SnoozeDB calls the STS API with an AssumeRole command. AWS receives the request and performs several checks. Is the calling account (SnoozeDB) authorized in the role's trust policy? Does the supplied External ID match the one recorded in that trust policy? Does the role still exist?
If everything checks out, STS returns three items: a temporary Access Key ID, a temporary Secret Access Key, and a Session Token. Together, these form a set of credentials valid for one hour.
SnoozeDB then uses these credentials to call rds:StopDBInstance on your staging database. From AWS's perspective, it is exactly as if the SnoozeDBAccessRole in your own account made the call. The credentials expire automatically after one hour, and SnoozeDB stores them nowhere. At the next operation, a fresh AssumeRole will be performed.
The trust policy: the client-side lock
The trust policy is what makes the mechanism secure. It is a rule attached to the IAM role in your account that explicitly states: "only AWS account number X may assume this role, and only if it provides External ID Y."
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
AWS: arn:aws:iam::<COMPTE_SNOOZEDB>:root
Action: sts:AssumeRole
Condition:
StringEquals:
sts:ExternalId: <VOTRE_UUID_UNIQUE>This template is generated automatically by SnoozeDB at connection time. You don't have to write anything yourself.
The important thing is that you retain full control. If you delete this role in your AWS console, SnoozeDB immediately loses all access to your account. No need to revoke keys, no need to contact us. The role is gone, the bridge is severed.
IAM External ID explained: protecting against the confused deputy
You may have noticed that alongside the Role ARN, SnoozeDB sends an External ID with every AssumeRole call. This is not an implementation detail; it is a defense against a well-documented attack AWS calls the "confused deputy."
The scenario goes like this. Imagine a third-party service (not SnoozeDB) that knows the format of your Role ARNs. If the trust policy only required the calling account's identity, that service could ask an intermediary (the "deputy") to assume your role on its behalf. The intermediary, authorized by the trust policy, would execute the request without realizing it is acting for the wrong party.
The External ID prevents this. It is a unique UUID generated by SnoozeDB when you connect your account. It is recorded in your role's trust policy AND stored on the SnoozeDB side in our database. To assume your role, this exact UUID must be provided. Anyone who knows your Role ARN but not your External ID will be denied access.
AWS explicitly recommends this practice for any SaaS service that assumes roles in its customers' accounts.
What SnoozeDB can do (and what it cannot)
The IAM role created in your account is locked to four actions, not one more.
rds:DescribeDBInstances to list your instances and check their status. rds:ListTagsForResource to read tags and apply your exclusion rules. rds:StartDBInstance to start an instance according to your schedule. rds:StopDBInstance to stop it.
These permissions grant zero access to the contents of your databases. SnoozeDB cannot run SQL queries, read your tables, access your snapshots, or modify your instance configuration. The role only covers start/stop state control.
The complete lifecycle
When you connect your account, SnoozeDB generates an External ID and a CloudFormation template. You deploy that template in your AWS account, which creates the IAM role with its trust policy. SnoozeDB verifies everything works by performing a first AssumeRole.
From then on, every time SnoozeDB needs to act on one of your instances (scan, start, stop), it performs an AssumeRole, obtains temporary credentials valid for one hour, uses them for the current operation, and lets them expire. Nothing is stored, nothing persists.
If you want to revoke access, you delete the CloudFormation stack in your AWS console. The role disappears, and SnoozeDB can no longer do anything on your account.
Why STS AssumeRole instead of sharing access keys?
There are other ways to grant cross-account access on AWS. We could have asked you to create a dedicated IAM user and send us its keys. That is simpler to set up, but it creates a real problem: permanent keys stored somewhere, that need to be rotated, and that represent a risk if leaked.
The AssumeRole mechanism eliminates that risk. Credentials are temporary by nature, generated on demand, and impossible to reuse after expiration. It is the pattern AWS recommends for all cross-account access, and the one used by virtually every serious SaaS tool that operates on its customers' infrastructure (Datadog, CloudHealth, Spot.io, etc.).
Further reading
- AWS Connection: permissions, CloudFormation deployment, and guarantees
- SnoozeDB Security: architecture, storage, and compliance
Ready to connect your AWS account?
Connection takes less than 5 minutes. No keys to share, no scripts to write.
14-day free trial