IAM Roles Anywhere: AWS Credentials for On-Prem Servers

During a migration your on-prem servers still need to read from S3, push to ECR, or write to DynamoDB, and the lazy answer is an IAM user with a long-lived access key baked into a config file. Do not do that. Use IAM Roles Anywhere: it lets a workload outside AWS present an X.509 certificate from your own certificate authority and exchange it for temporary credentials through STS, so there are no static keys to leak, rotate by hand, or find in a Git history three years from now.
This is the hybrid-identity bridge most teams need for the months or years that on-prem and cloud run side by side. It is worth setting up properly, because the default trust boundary is wider than you expect and the thing you are now on the hook for is a PKI.
What IAM Roles Anywhere actually does
Three objects do the work, and you need all three.
- Trust anchor. A reference to the CA that issues your workload certificates. It is either an AWS Private CA resource or an external CA certificate you upload. <cite>Workloads outside AWS authenticate with the trust anchor using certificates issued by the trusted CA, in exchange for temporary AWS credentials, and you can have several trust anchors in one account.</cite> (docs)
- IAM role. A normal role with a permissions policy, except its trust policy trusts the
rolesanywhere.amazonaws.comservice principal instead of an account or a web identity. <cite>A trust anchor is tied to the IAM role via the aws:SourceArn condition key that uses the trust anchor's ARN as its value in the role's trust policy.</cite> (docs) - Profile. The glue that says which roles this certificate population may assume, optionally narrowed by a session policy. <cite>A profile can have many IAM roles but only one session policy, and any session returned by a CreateSession call that references the profile has its permissions limited by that session policy.</cite> (docs)
On the server, a small binary called the credential helper (aws_signing_helper) does the certificate signing and the STS exchange, then feeds credentials to the AWS CLI or an SDK. That is the whole model: certificate in, temporary credentials out.
Set it up: trust anchor, role, profile
Create the trust anchor first. <cite>The trust anchor requires you to reference a certificate authority that IAM Roles Anywhere will use to validate your authentication requests, either an AWS Private CA resource or an external CA.</cite> (docs) If you already run a corporate PKI, point it there. If you do not, AWS Private CA is the path of least resistance, at the cost of a per-CA monthly charge plus per-certificate fees.
Next, the role trust policy. This is the part people copy-paste wrong.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "rolesanywhere.amazonaws.com" },
"Action": [
"sts:AssumeRole",
"sts:TagSession",
"sts:SetSourceIdentity"
],
"Condition": {
"ArnEquals": {
"aws:SourceArn": [
"arn:aws:rolesanywhere:us-east-1:123456789012:trust-anchor/TA_ID"
]
}
}
}
]
}
All three actions matter. sts:TagSession is what lets certificate attributes flow through as session tags, and sts:SetSourceIdentity is what writes an identity you can later see in CloudTrail. Then create the profile, attach the role, and add a session policy if you want to narrow permissions further for this certificate population.
The order is deliberate: trust anchor, then a role whose trust policy references that trust anchor's ARN, then a profile that lists the role.
Lock the trust policy down with certificate attributes
Here is the trap. <cite>For IAM Roles Anywhere the trust boundary is established at the account level, which means certificates issued by any trust anchor in the account can be used to assume any target role in that same account unless you specify conditions in the role's trust policy.</cite> (docs) And the documentation is blunt about the consequence: <cite>without a Condition statement present in a role trust policy, any valid certificate from the CA used as the trust anchor, or CAs subordinate to that trust anchor, may be used to assume a role.</cite> (docs)
Translation: if your CA issues certificates for a hundred machines and your trust policy only checks aws:SourceArn, every one of those hundred machines can assume the role. That is almost never what you want.
Fix it by matching on certificate attributes that the profile maps into principal tags. The common case is pinning a role to a specific certificate subject common name:
{
"Effect": "Allow",
"Principal": { "Service": "rolesanywhere.amazonaws.com" },
"Action": ["sts:AssumeRole", "sts:TagSession", "sts:SetSourceIdentity"],
"Condition": {
"StringEquals": {
"aws:PrincipalTag/x509Subject/CN": "application-1.com"
},
"ArnEquals": {
"aws:SourceArn": ["trust-anchor-arn"]
}
}
}
That is the pattern AWS Prescriptive Guidance recommends so that <cite>each application can assume a specific role because the condition requires a specific certificate common name, which helps prevent unauthorized access because the application cannot assume any role linked to the target profile.</cite> (docs)
One subtlety that will waste an afternoon if you miss it: the condition only works if the profile's attribute mapping actually maps that field. <cite>If a profile is used with an attribute mapping that lacks the relevant specifier in its mapping rules, the condition in the trust policy evaluates as false because there is no value mapped to the principal tag.</cite> (docs) Profile mapping and trust policy condition are two halves of the same control. Change one, check the other.
Delivering credentials on the host
The credential helper has three modes and they are not interchangeable. Download the aws_signing_helper binary for your platform (Linux, Windows, and macOS builds are published, currently release 1.8.5) and verify the SHA256 before you ship it. (docs)
The one-shot invocation looks like this:
./aws_signing_helper credential-process \
--certificate /path/to/certificate.pem \
--private-key /path/to/private-key.pem \
--trust-anchor-arn arn:aws:rolesanywhere:us-east-1:123456789012:trust-anchor/TA_ID \
--profile-arn arn:aws:rolesanywhere:us-east-1:123456789012:profile/P_ID \
--role-arn arn:aws:iam::123456789012:role/on-prem-app
Which mode you pick depends on what is consuming the credentials:
| Mode | Refresh | How apps consume it | Best for |
|---|---|---|---|
credential-process | No, signs on each call | credential_process line in a CLI profile | Single commands and scripts |
update | Yes | Writes to the shared credentials file on disk | AWS CLI usage |
serve | Yes | Local endpoint, IMDSv2-compatible | SDK applications |
<cite>The serve command vends temporary credentials through a local endpoint on localhost that is compatible with IMDSv2, which lets AWS SDKs discover the credentials automatically.</cite> (docs) For a long-running service that uses an SDK, serve is usually the cleanest: the app behaves as if it were running on an EC2 instance with an instance profile, and you keep credentials in memory rather than on disk.
Session length is set on the profile. <cite>The duration can range from 900 seconds up to 43200 seconds, and if you do not provide a value the default is 3600.</cite> (docs) One cap to remember: <cite>if the requested duration is greater than the MaxSessionDuration set on the role, the API returns an error.</cite> (docs) So a 12-hour profile against a role with the default 1-hour max will simply fail.
The trade-off, and what it costs later
IAM Roles Anywhere removes static keys. In exchange, you now operate certificate lifecycle for every workload that uses it. That is a real cost, not a footnote.
You own rotation. AWS guidance is to issue short-lived workload certificates where you can tolerate it. <cite>AWS recommends certificates expire as quickly as your operational tolerances allow, and if your PKI issues or distributes certificates manually you may need longer-lived certificates to ease the operational burden.</cite> (blog) If you do not have automated issuance, you are trading key rotation for certificate rotation, and manual certificate rotation at scale is its own on-call burden.
You own revocation. When a private key is compromised you revoke the certificate and publish a CRL. <cite>IAM Roles Anywhere checks certificates against your CRL, which you import using the ImportCrl API or the import-crl CLI command, after which a copy is hosted within IAM Roles Anywhere and validated before credentials are issued.</cite> (blog) Hosting the CRL inside the service is deliberate, but updating it is on you.
Certificate attributes have hard requirements. <cite>End-entity certificates must be X.509v3 with basic constraints CA:false, key usage including Digital Signature, and a signing algorithm of SHA256 or stronger, because MD5 and SHA1 are rejected.</cite> (blog) The trust anchor certificate has its own rules, including that it must not be a public CA or issued by one. A PKI that does not meet these will fail at the exchange, not at setup.
One upside worth planning around: <cite>the serial number of the certificate is used as the role session name, so it can associate CloudTrail events back to the exact certificate used to assume the role.</cite> (blog) Issue unique serial numbers and your audit trail points at individual machines for free.
If you are weighing IAM Roles Anywhere as part of a larger datacentre exit, it fits naturally into the identity workstream of a planned cloud migration, and getting the trust policies and PKI right is squarely the kind of thing our security and compliance work covers.
What to check before you change anything
Before you touch a trust policy or hand out a certificate, confirm these.
- Do you have a CA you control? Not a public one. If not, decide between AWS Private CA and standing up your own before anything else.
- Can you automate issuance and rotation? If the honest answer is no, your certificates will be long-lived, and you should plan revocation and monitoring accordingly rather than pretend otherwise.
- Is every role trust policy conditioned on a certificate attribute? An account-level trust boundary means an unconditioned policy is a blank cheque to the whole CA.
- Does the profile attribute mapping match every condition you rely on? Test one role end to end before you template it across fleets.
- Does the role's MaxSessionDuration cover the profile's requested duration? Otherwise sessions fail in production, not in your test.
Get those five right and the migration-era problem of on-prem workloads reaching into AWS stops being a pile of static keys and becomes a certificate you can revoke in one API call.
Frequently asked questions
Does IAM Roles Anywhere replace an IAM user with access keys?
Yes, that is the point. Instead of embedding a long-lived access key and secret on an on-prem server, the server presents a certificate and receives temporary STS credentials. There are no static secrets to rotate or leak, though you take on certificate lifecycle management in return.
Can one certificate assume any role in the account?
By default, close to it. <cite>The trust boundary is at the account level, so a certificate from any trust anchor can assume any role in that account unless the role's trust policy adds a condition.</cite> (docs) Always constrain the trust policy with certificate-attribute conditions such as aws:PrincipalTag/x509Subject/CN, and make sure the profile maps that attribute.
How long are the temporary credentials valid?
The profile sets the session duration. <cite>It can range from 15 minutes to 12 hours, defaulting to one hour if unset.</cite> (docs) The requested duration cannot exceed the role's MaxSessionDuration, or the session request fails.
How do I revoke access for a compromised host?
Revoke its certificate in your CA and publish an updated CRL, then import that CRL into IAM Roles Anywhere. <cite>The service validates certificates against the imported CRL before issuing credentials, and hosts its own copy of the CRL.</cite> (blog) Revocation only works if your CA certificate was configured with CRL signing in the first place, so set that up before you need it.
Which credential helper mode should a long-running service use?
Use the serve command. <cite>It exposes a local IMDSv2-compatible endpoint that AWS SDKs discover automatically, and keeps credentials in memory.</cite> (docs) Use credential-process for scripts and single commands, and update when you specifically need the credentials written to the shared credentials file for the AWS CLI.


