Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

By updated

Improve this page

HashiCorp Vault logo

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_role and federation_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_user accounts, 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 keysWith Vault AWS
Unlimited lifetimeTTL 1-12h max
Shared through secrets or env varsGenerated on demand
Rare manual rotationAutomatic rotation through the TTL
Compromise = permanent accessCompromise = 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:

TypeHow it worksTypical TTLUse case
iam_userVault creates an IAM user + access key∞ (but a Vault lease)Legacy, complex permissions
assumed_roleVault assumes an IAM role through STS1-12h (AWS bound)Recommended - standard
federation_tokenVault generates a federated tokenUp to 36hAWS 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.

Terminal window
vault secrets enable aws

Step 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:

Terminal window
vault write aws/config/root \
access_key="AKIAIOSFODNN7EXAMPLE" \
secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
region="eu-west-1"

Whichever 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:AssumeRole on 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.

Terminal window
vault write -force aws/config/rotate-root

Step 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:

Terminal window
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"
}
]
}

Step 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.

Terminal window
vault read aws/creds/deploy

Output (for assumed_role):

Key Value
--- -----
lease_id aws/creds/deploy/abcd1234
lease_duration 1h
lease_renewable true
access_key ASIAIOSFODNN7EXAMPLE
secret_key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
security_token FwoGZXIvYXdzEBY...
arn arn:aws:sts::123456789012:assumed-role/DeployRole/vault-token-xyz

What happens:

  1. Vault calls sts:AssumeRole with the configured IAM role
  2. AWS returns temporary STS credentials
  3. Vault creates a lease and returns the credentials
  4. The application uses these credentials
  5. 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.

Terminal window
export AWS_ACCESS_KEY_ID="ASIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export AWS_SESSION_TOKEN="FwoGZXIvYXdzEBY..."
aws s3 ls

What 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.

Terminal window
vault write aws/static-roles/ci-user \
name="ci-user" \
username="ci-pipeline-user" \
rotation_period="24h"

Reading:

Terminal window
vault read aws/static-creds/ci-user

Difference 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.

Terminal window
vault lease revoke aws/creds/deploy/abcd1234

What happens depending on the type:

  • iam_user: Vault deletes the access key and the IAM user
  • assumed_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.

Terminal window
vault lease revoke -prefix aws/creds/deploy

Which 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.

policy-deploy.hcl
# Get AWS credentials for the deployment
path "aws/creds/deploy" {
capabilities = ["read"]
}
# Renew its own leases
path "sys/leases/renew" {
capabilities = ["update"]
}
Terminal window
vault policy write deploy policy-deploy.hcl

How 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 AWS
on:
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-bucket

GitLab 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-bucket

On 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.

SymptomLikely causeSolution
permission deniedMissing Vault policyCheck aws/creds/<role>
AccessDenied on the AWS sideIncorrect trust policyCheck sts:AssumeRole
ExpiredTokenTTL exceededRenew or get new creds
InvalidIdentityTokenClock out of syncCheck NTP on Vault
IAM user not deletedMissing IAM permissionsCheck 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.

Terminal window
# Check the configuration
vault read aws/config/root
# List the roles
vault list aws/roles
# Check a specific role
vault read aws/roles/deploy
# Test an issuance
vault read aws/creds/deploy
# Check the active leases
vault list sys/leases/lookup/aws/creds/deploy

What 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.

ResponsibilityVaultYou
Generate STS credentialsYesNothing to do
Manage TTLs and leasesYesNothing to do
Create the target IAM rolesNoYes
Configure the trust policiesNoYes
Revoke STS credentials on the AWS sideNoN/A (expire naturally)
Monitor AWS usageNoYes (CloudTrail)

Key points

  1. assumed_role: recommended mode, no IAM entity creation
  2. iam_user: Vault creates a temporary IAM user and deletes it when the lease expires or is revoked
  3. federation_token: for the AWS console or specific workloads
  4. Short TTLs: 1-4h, Vault cannot recall STS credentials
  5. Trust policy: the IAM role must allow Vault to assume it
  6. Static role: rotation of an existing key (a compromise for legacy)
  7. Audit: Vault traces the issuance, CloudTrail traces the usage
  8. Proof expected by SOCLE: default_sts_ttl and max_sts_ttl prove SOCLE-SEC-ROT-1 (in French); the record of a vault lease revoke on an iam_user role, with the stated limit for STS sessions, proves SOCLE-SEC-ROT-3 (in French); the rotation_period of 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

3 questions
3 min
70% to pass

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

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn