The AWS engine of Vault generates temporary IAM credentials on demand. No more permanent access keys in the code: each workload gets its own credentials, valid for a few hours at most. IAM (Identity and Access Management) is the AWS service that defines who may do what in an account, and an access key is the pair of values (key ID and secret) that proves an identity to the AWS API.
This guide covers the three types of AWS credentials that Vault can issue
and when to use each. It teaches what is specific to AWS: root configuration,
credential_type, trust policy, what revocation can and cannot do. The general
lease mechanism is not repeated here; see the lesson on
leases, dynamic roles and static roles.
What you will learn
By the end of this guide you will be able to:
- Configure the AWS engine in Vault, with its own root credentials
- Choose between
iam_user,assumed_roleandfederation_token - Create a role and issue temporary STS credentials
- Explain what revocation can and cannot undo on the AWS side
- SOCLE anchoring: this guide implements SOCLE-SEC-ROT-1 (in French), STS TTLs bounded per role, SOCLE-SEC-ROT-3 (in French), emergency revocation (limited to
iam_useraccounts, not to STS sessions), and SOCLE-SEC-ROT-5 (in French), the automatic rotation of a static role
Why use dynamic credentials for AWS?
A permanent AWS access key carries no expiration date: it stays valid as long as nobody deletes it in IAM. The Vault AWS engine replaces this model with issuance on demand, where each credential is attached to a lease (the record Vault keeps for it, with its expiry) and stops working when its TTL (time to live) runs out. The table below compares what you lose and what you gain on the four points that matter: lifetime, distribution, rotation and traceability.
| Permanent access keys | With Vault AWS |
|---|---|
| Unlimited lifetime | TTL 1-12h max |
| Shared through secrets or env vars | Generated on demand |
| Rare manual rotation | Automatic rotation through the TTL |
| Compromise = permanent access | Compromise = access limited in time |
| Audit: "who has this key?" | Audit: "which workload asked, and when" |
Prerequisites
Before you start, you need three notions. A lease ties a credential to its expiry, the TTL is the lifetime granted (and the max TTL the ceiling renewals cannot pass), and revocation ends a lease before its expiry. They are presented in the lesson on leases, dynamic roles and static roles. You also need to be able to write a policy, a set of rules that says which paths a client may use, to control who can request these credentials.
- Vault installed and started
- Admin access to configure the engine
- An AWS account with IAM permissions to create credentials
Which credential type: iam_user, assumed_role or federation_token?
Vault can issue three types of AWS credentials, and the choice is made at
the Vault role level through the credential_type parameter. A Vault
role is a named configuration that says what the engine may issue. The type
cannot be changed afterwards without recreating the role, and it determines
both the maximum lifetime of the credentials and what Vault can do at
revocation. Read this table starting from the use case column:
| Type | How it works | Typical TTL | Use case |
|---|---|---|---|
iam_user | Vault creates an IAM user + access key | ∞ (but a Vault lease) | Legacy, complex permissions |
assumed_role | Vault assumes an IAM role through STS | 1-12h (AWS bound) | Recommended - standard |
federation_token | Vault generates a federated token | Up to 36h | AWS console, human access |
STS (Security Token Service) is the AWS service that issues temporary
credentials; assuming a role means asking STS for a session with that role's
permissions. The maximum TTLs depend on the AWS configuration of the
target STS role and on the default_sts_ttl / max_sts_ttl parameters set
in Vault. The values above are usual bounds, not universal limits.
Step 1: enable the AWS engine
The AWS engine is not enabled by default in Vault, you must mount it
explicitly. Without the -path option, it is mounted on the aws/ path: all
the paths in this guide (aws/config/root, aws/roles/…, aws/creds/…) follow
from it. If you need to manage several AWS accounts, mount one instance of
the engine per account with -path=aws-prod, -path=aws-dev, and adapt the
paths and the policies accordingly.
vault secrets enable awsStep 2: configure the root credentials
Vault needs credentials of its own to call the AWS APIs, called the root credentials of the engine (they are not the AWS account's root user). Two options exist, depending on where Vault runs:
vault write aws/config/root \ access_key="AKIAIOSFODNN7EXAMPLE" \ secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \ region="eu-west-1"If Vault runs on AWS with an IAM role attached (instance profile, task role, IRSA), you can use that role directly:
vault write aws/config/root \ region="eu-west-1" # No credentials: Vault uses the instance's roleWhichever option you pick, the configured root account or role must have the IAM permissions needed for the type of credential you are going to generate:
- iam_user:
iam:CreateUser,iam:CreateAccessKey,iam:DeleteUser, etc. - assumed_role:
sts:AssumeRoleon the target roles - federation_token:
sts:GetFederationToken
How do you rotate the root credentials?
If you configured aws/config/root with a static access key, renew it
right away: Vault generates a new key for that same IAM user and forgets
the old value, which may remain known to the person who typed the command.
The command is called rotate-root, which is HashiCorp's name for this
operation and a command name, so it stays as is.
vault write -force aws/config/rotate-rootStep 3: create a role
A Vault role describes what the AWS engine is allowed to issue: the
credential_type, the IAM target and the TTLs. As long as no role
exists, vault read aws/creds/<name> fails, even with a valid root
configuration. The three tabs below show the same operation for the three
credential_type values: read the one that matches the mode you chose, the
parameters are not interchangeable.
The Vault role references an existing IAM role in AWS:
vault write aws/roles/deploy \ credential_type="assumed_role" \ role_arns="arn:aws:iam::123456789012:role/DeployRole" \ default_sts_ttl="1h" \ max_sts_ttl="4h"AWS prerequisite: the DeployRole role must have a trust policy (the
IAM document that says who may assume the role) allowing Vault (through its
role or user) to assume it:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789012:user/vault-aws" }, "Action": "sts:AssumeRole" } ]}Vault creates a temporary IAM user with an inline policy (a permission document attached directly to that user, as opposed to a managed policy that exists on its own and can be shared):
vault write aws/roles/s3-readonly \ credential_type="iam_user" \ policy_document=-<<EOF{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject", "s3:ListBucket"], "Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"] } ]}EOFOr with managed policies:
vault write aws/roles/ec2-admin \ credential_type="iam_user" \ policy_arns="arn:aws:iam::aws:policy/AmazonEC2FullAccess"For console access or specific workloads:
vault write aws/roles/console-readonly \ credential_type="federation_token" \ policy_document=-<<EOF{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["ec2:Describe*", "s3:List*"], "Resource": "*" } ]}EOFStep 4: issue temporary credentials
Issuance goes through a read of the aws/creds/<role name> path, not a
write: each vault read produces a new set of credentials and a new
lease. Running the command again therefore does not return the previous
value, it creates one more, which matters when the role is iam_user (each
read creates an IAM user) or when AWS limits the number of sessions.
vault read aws/creds/deployOutput (for assumed_role):
Key Value--- -----lease_id aws/creds/deploy/abcd1234lease_duration 1hlease_renewable trueaccess_key ASIAIOSFODNN7EXAMPLEsecret_key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEYsecurity_token FwoGZXIvYXdzEBY...arn arn:aws:sts::123456789012:assumed-role/DeployRole/vault-token-xyzWhat happens:
- Vault calls
sts:AssumeRolewith the configured IAM role - AWS returns temporary STS credentials
- Vault creates a lease and returns the credentials
- The application uses these credentials
- At expiry, the STS credentials become invalid (on the AWS side)
How do you use the credentials?
The AWS CLI and the SDKs read these three environment variables
before the ~/.aws/credentials file: a profile already configured on the
machine is therefore ignored as long as they are set.
export AWS_ACCESS_KEY_ID="ASIAIOSFODNN7EXAMPLE"export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"export AWS_SESSION_TOKEN="FwoGZXIvYXdzEBY..."
aws s3 lsWhat is a static role on the AWS engine?
A static role creates nothing: it takes over an IAM user that already
exists and merely renews its access key on the period you set. The identity
therefore stays the same in CloudTrail (the AWS audit log) and in your IAM
policies, only the key changes. Like iam_user, it delivers an access key
without a session token (an IAM user key is not an STS session), which is
why it is used for tools that cannot handle STS.
vault write aws/static-roles/ci-user \ name="ci-user" \ username="ci-pipeline-user" \ rotation_period="24h"Reading:
vault read aws/static-creds/ci-userDifference with a dynamic role:
- Same IAM user (
ci-pipeline-user) - Access key renewed every 24 h
- No session token
What does revocation do on the AWS side?
Revoking, in Vault, means closing a lease. What this produces on the AWS
side depends entirely on the credential_type: Vault can delete an IAM
entity that it created, but it cannot recall an STS session that has
already been issued. Telling the two apart avoids the false security of
believing that a vault lease revoke cuts access instantly in every case.
Revoke a lease
The lease_id is the one returned at issuance: without it, the command has
nothing to revoke.
vault lease revoke aws/creds/deploy/abcd1234What happens depending on the type:
iam_user: Vault deletes the access key and the IAM userassumed_role: Vault marks the lease as revoked (the STS credentials expire naturally, there is no AWS-side revocation)federation_token: same as assumed_role
Revoke all the credentials of a role
The -prefix option revokes all the leases issued under that path at
once: it is the incident response gesture, with no confirmation and no way
back.
vault lease revoke -prefix aws/creds/deployWhich Vault policy gives access to AWS credentials?
Enabling the engine is not enough: without a policy, an authenticated
client gets permission denied on aws/creds/deploy. The policy below
grants the strict minimum, the read capability on a single role,
which prevents a workload from picking the credentials of another role even if
it knows its name. Note that no capability is given on aws/roles/*: the
holder of this policy consumes credentials, it cannot change the engine's
configuration.
# Get AWS credentials for the deploymentpath "aws/creds/deploy" { capabilities = ["read"]}
# Renew its own leasespath "sys/leases/renew" { capabilities = ["update"]}vault policy write deploy policy-deploy.hclHow do you use these credentials in CI/CD?
CI/CD is where the gain is clearest: the pipeline no longer needs an AWS
secret stored in the platform, it gets credentials valid for the duration of
the job. The two examples below rely on Vault's JWT/OIDC auth (the CI
platform issues a signed token that proves the identity of the workflow, and
Vault checks it). The Vault role (github-deploy, gitlab-deploy) must exist
on the Vault side with the bound claims (the conditions on the token's
content, such as repository and branch) that match, otherwise the login is
refused.
GitHub Actions
This workflow stores no AWS secret: GitHub's OIDC token serves as proof
of identity to Vault, which returns STS credentials injected into the job's
environment. The id-token: write permission is indispensable for GitHub to
issue this token, and the actions are pinned by SHA so that a tag moved on
the provider's side does not change the code that runs.
name: Deploy to AWSon: push: branches: [main]
permissions: {}
jobs: deploy: runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Import Secrets from Vault uses: hashicorp/vault-action@892a26828f195e65540a40b4768ae4571f51ebfc # v4.0.0 with: url: https://vault.example.com method: jwt role: github-deploy secrets: | aws/creds/deploy access_key | AWS_ACCESS_KEY_ID ; aws/creds/deploy secret_key | AWS_SECRET_ACCESS_KEY ; aws/creds/deploy security_token | AWS_SESSION_TOKEN
- name: Deploy run: | aws s3 sync ./dist s3://my-app-bucketGitLab CI
Without a dedicated action, the job calls the Vault CLI directly. GitLab issues
the job's identity token through the id_tokens: keyword, which declares a
variable (here VAULT_ID_TOKEN) and its audience (aud), that is the
recipient of the token: your Vault URL. This variable replaces the former
CI_JOB_JWT, deprecated since GitLab 15.9 and removed in 17.0. The eval
then turns the JSON response into three export lines, which assumes that
vault and jq are present in the job image.
deploy: stage: deploy id_tokens: VAULT_ID_TOKEN: aud: https://vault.example.com script: - export VAULT_ADDR="https://vault.example.com" - export VAULT_TOKEN=$(vault write -field=token auth/jwt/login role=gitlab-deploy jwt=$VAULT_ID_TOKEN) - eval $(vault read -format=json aws/creds/deploy | jq -r '.data | "export AWS_ACCESS_KEY_ID=\(.access_key) AWS_SECRET_ACCESS_KEY=\(.secret_key) AWS_SESSION_TOKEN=\(.security_token)"') - aws s3 sync ./dist s3://my-app-bucketOn the Vault side, the token's audience must match the one expected by the
jwt method (the gitlab-deploy role validates it with its bound
claims). The detail of id_tokens and of the claims to filter is in
Securing secrets in your CI/CD pipelines.
What if it does not work?
Errors fall into two families that you must tell apart before searching: those
returned by Vault (permission denied, missing policy) and those returned
by AWS (AccessDenied, ExpiredToken), which come up through Vault or
through the AWS CLI. The original message therefore tells you which side to
look at: the Vault policy, or the IAM trust policy.
| Symptom | Likely cause | Solution |
|---|---|---|
permission denied | Missing Vault policy | Check aws/creds/<role> |
AccessDenied on the AWS side | Incorrect trust policy | Check sts:AssumeRole |
ExpiredToken | TTL exceeded | Renew or get new creds |
InvalidIdentityToken | Clock out of sync | Check NTP on Vault |
| IAM user not deleted | Missing IAM permissions | Check iam:DeleteUser, iam:DeleteAccessKey |
How do you debug the engine?
Run these commands in order: each one eliminates a hypothesis, from the
engine configuration down to the leases actually open. Note that
vault read aws/config/root never returns the secret key, only the region
and the access key.
# Check the configurationvault read aws/config/root
# List the rolesvault list aws/roles
# Check a specific rolevault read aws/roles/deploy
# Test an issuancevault read aws/creds/deploy
# Check the active leasesvault list sys/leases/lookup/aws/creds/deployWhat does the AWS engine not do?
The AWS engine handles the issuance and lifecycle of credentials,
nothing more. The entire IAM side upstream (target roles, policies, trust
policies) remains your responsibility, and Vault will not warn you if it is
too permissive: an AdministratorAccess IAM role issued as temporary
credentials is still administrator access. This split of responsibilities is
the most frequent source of error on this engine.
| Responsibility | Vault | You |
|---|---|---|
| Generate STS credentials | Yes | Nothing to do |
| Manage TTLs and leases | Yes | Nothing to do |
| Create the target IAM roles | No | Yes |
| Configure the trust policies | No | Yes |
| Revoke STS credentials on the AWS side | No | N/A (expire naturally) |
| Monitor AWS usage | No | Yes (CloudTrail) |
Key points
- assumed_role: recommended mode, no IAM entity creation
- iam_user: Vault creates a temporary IAM user and deletes it when the lease expires or is revoked
- federation_token: for the AWS console or specific workloads
- Short TTLs: 1-4h, Vault cannot recall STS credentials
- Trust policy: the IAM role must allow Vault to assume it
- Static role: rotation of an existing key (a compromise for legacy)
- Audit: Vault traces the issuance, CloudTrail traces the usage
- Proof expected by SOCLE:
default_sts_ttlandmax_sts_ttlprove SOCLE-SEC-ROT-1 (in French); the record of avault lease revokeon aniam_userrole, with the stated limit for STS sessions, proves SOCLE-SEC-ROT-3 (in French); therotation_periodof a static role proves SOCLE-SEC-ROT-5 (in French)
Knowledge check
Check that the essentials of this lesson are in place. The questions only cover what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: The secret zero problem: who hands out the first secret?
A new full quiz with random questions
Work again only on the questions you missed