Serverless Deploy IAM Role: What It Is and Why It Matters
September 2026 · 7 min read · Surya
A credential can sit in a CI log for years before anyone notices, because nobody is looking. Deploy roles are assumed once per pipeline run and forgotten, which is exactly why they never get tightened. The serverless deploy IAM role is the identity your pipeline uses to push code, not the role your function runs as, and conflating the two is how it ends up wearing AdministratorAccess.
A serverless deploy IAM role is the identity your pipeline assumes, not the identity your function runs as
Two roles. Two entirely different threat models. Most teams only know they have one.
When a serverless framework pushes your artifact, it calls sts:AssumeRole on a role before it can do anything else. That role appears in --role flags, in provider.iam.deployRole style config, or in whichever CI secret your pipeline reads at the top of the job. It is the serverless deploy IAM role. Its job is to let a build system rewrite a CloudFormation stack, upload a zip to a deployment bucket, and update a function's code. That is the whole job.
The execution role is different. It is what your Lambda function assumes at runtime, once per invocation, to read from a queue or write to a table. It is scoped to the request path. It has nothing to do with your build system.
Here is where they live in the flow:
CI runner
└─ assumes DEPLOY role ──▶ writes artifact to S3
──▶ calls CloudFormation / Lambda
──▶ updates the stack
Lambda function (invoked later)
└─ assumes EXECUTION role ──▶ reads queue, writes table
──▶ scoped to the request path
The blast radius of the deploy role is your build system, not your request path. That distinction matters because the deploy role usually has far more reach than the execution role, and far less scrutiny. Nobody reviews it in a pull request. It was configured once, in a hurry, and it has not been opened since.
The permissiveness creep is real, and it starts with the first failed deploy
The loop looks the same in every account. A deploy fails on an API call nobody anticipated. Someone pastes in a broader policy, or attaches AdministratorAccess entirely, to unblock the release. The release ships. The policy stays. Nothing breaks, so nothing gets tightened.
The calls that keep showing up in this role are always the same handful. S3 put, get, and delete on the deployment bucket. Lambda and CloudFormation writes. CloudWatch Logs. And iam:PassRole, which is where things get interesting.
Remove iam:PassRole and the role shrinks to something defensible. iam:PassRole lets a caller hand an arbitrary role to an AWS service. A CI runner holding iam:* can mint whatever identity it wants and hand it to Lambda. That makes the deploy role the highest-value credential in most accounts. It also, in a large number of setups, sits in a plaintext CI secret that anyone with repo read access can see.
What a scoped deploy role policy looks like{ "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::my-deploy-bucket/*", "Condition": { "StringLike": { "s3:prefix": ["artifacts/${aws:PrincipalTag/repo}/*"] } } }One bucket, one prefix, no wildcards on the resource. The
s3:prefixcondition means a leaked credential from repo A cannot overwrite repo B's artifacts. This is the shape. Copy it, then remove everything else the role does not strictly need.
Scoping the policy is half the job. Storing the credential safely is the other half. If your deploy role lives in a plaintext CI secret, the policy above buys you less than you think. Storafleet's IAM-role connect is built around the idea that credentials should be assumed, not pasted, and that what you store should be encrypted. That is the moment to think about it: when you are staring at a role that can mint identities and wondering where its keys actually live.
Assume-role beats long-lived keys, and it costs you one thing
Short-lived credentials assumed via a trust policy are strictly better than a static AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY pair. No key to rotate on a schedule nobody honors. No key sitting in a CI variable for two years. The credential expires in minutes and a trust relationship replaces it.
That is not a controversial position. It is also not free.
The cost is that the trust boundary moves. Now the question is not "who has the key" but "who can assume the role." If your OIDC provider is misconfigured, or your CI runner's identity is too broad, you have moved the problem rather than solved it. A trust policy that accepts any principal from your CI provider's issuer is not better than a static key. It is the same risk with a shorter expiry.
And keeping credentials on hardware you control is a stronger answer than anything we offer. If your deploy role's keys never leave a machine you own, you are not trusting a vendor's database. That is a real advantage and we do not have it. Our IAM-role connect covers AWS only. It does not extend to R2, B2, or any of the other S3-compatible providers we connect, because those providers do not offer the same keyless mechanism.
Where we fit, and where we are genuinely the wrong tool for this
Start with rclone, because for a lot of people it is the honest answer. Free, permanently. No account, no vendor, no subscription. It runs inside your own security boundary, so the credentials never leave your control. It mounts. It is scriptable. It composes with cron, systemd, CI pipelines, shell scripts, and Makefiles in a way a web console never will. It has been maintained for over a decade and supports 70+ backends, including the consumer clouds we deliberately do not touch.
If your deploy-adjacent storage workflow is scripted, scheduled, or needs to run headless, rclone is probably what you want and we are probably not. That is not a hedge. It is the correct answer for a large fraction of the people reading this.
What Storafleet actually is: a control plane for buckets you already own. We connect 50+ S3-compatible providers plus any custom endpoint. We migrate and sync between them, one-time or scheduled. Credentials are encrypted at rest with AES-256-GCM and per-namespace HKDF keys, and AWS can be connected keylessly via IAM-role assumption. We never charge per gigabyte to move data; migrated bytes are unmetered on every tier, including Free. Tiers differ on concurrent migration jobs and scheduled sync.
What we are not: there is no FUSE, so no local mount. No Google Drive, Dropbox, OneDrive, SFTP, WebDAV, or Box. We are a console, not a command, so we do not compose with your Makefile. We have no published case studies and no named customers, and we will not invent them. We are a small team and Storafleet started in 2026, so on "will this still exist in five years," a decade-old open-source project has a better answer than we do.
The verdict, in plain words.
- If your budget for this is zero, rclone wins and the conversation is over.
- If you need storage mounted as a filesystem, use
rclone mount, Mountain Duck, or ExpanDrive. - If you need Google Drive, Dropbox, OneDrive, or SFTP, use rclone or MultCloud.
- If your credentials must never leave hardware you control, use rclone, or our IAM-role connect if it is AWS only.
- If everything lives in one cloud and always will, that provider's own console is free and more current than we are.
- If the workflow is entirely code and always will be, a CLI composes and a console does not.
Who should use us here: teams running multiple S3-compatible providers who need to move data between them without writing and maintaining that plumbing themselves. If you are syncing between R2 and B2 on a schedule, or migrating a bucket from Wasabi to Hetzner, and you want that visible in one place with costs attached, that is the job. It is a flat subscription. We do not meter the bytes.