A fast, single-binary CLI tool that automates AWS MFA credential refresh. Written in Go.
Reads long-term IAM credentials from ~/.aws/credentials, calls AWS STS with your MFA token, and writes the resulting short-term credentials back — so every AWS SDK and CLI tool picks them up automatically.
This is a Go rewrite of broamski/aws-mfa
Via go install:
go install github.com/MarinX/aws-mfa/cmd/aws-mfa@latestPre-built binaries (Linux, macOS, Windows) are available on the Releases page.
From source:
git clone https://github.com/MarinX/aws-mfa
cd aws-mfa
go build -o aws-mfa ./cmd/aws-mfaRun the interactive setup:
aws-mfa setupProfile name [default]: myprofile
AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Or add the section to ~/.aws/credentials manually:
[default-long-term]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
aws_mfa_device = arn:aws:iam::123456789012:mfa/myuserYour MFA device ARN is found in the AWS Console under
IAM → Users → [your user] → Security credentials → Multi-factor authentication (MFA)
Open your authenticator app (Google Authenticator, Authy, 1Password, etc.) and read the current code for your AWS account.
aws-mfaEnter MFA token for arn:aws:iam::123456789012:mfa/myuser: 123456
Successfully refreshed credentials for [default].
Credentials valid until 2026-06-18 02:00:00 CEST (43200 seconds from now).
Short-term credentials are now in the [default] section of ~/.aws/credentials and will be used automatically by the AWS CLI, SDKs, and any other AWS tooling.
The tool maintains two sections per profile in ~/.aws/credentials:
| Section | Purpose |
|---|---|
[default-long-term] |
Your permanent IAM access key — never used directly for API calls |
[default] |
Temporary STS credentials written by aws-mfa — expire after 12 hours by default |
On each run, the tool checks whether the short-term credentials are still valid. If they are, it exits immediately with the remaining time. If they are expired (or missing), it prompts for an MFA token, calls STS, and writes fresh credentials.
aws-mfa [flags]
aws-mfa setup
| Flag | Default | Description |
|---|---|---|
--profile |
default |
AWS profile name |
--device |
(auto) | MFA device ARN |
--token |
(prompt) | MFA token — skip interactive prompt |
--duration |
43200 |
Credential validity in seconds (3600 when assuming a role) |
--assume-role |
— | IAM role ARN to assume via sts:AssumeRole |
--external-id |
— | ExternalId for cross-account AssumeRole |
--role-session-name |
OS username | Session name for AssumeRole |
--region |
us-east-1 |
AWS region for the STS endpoint |
--long-term-suffix |
long-term |
Suffix for the long-term section (none = no suffix) |
--short-term-suffix |
(empty) | Suffix for the short-term section (none = no suffix) |
--force |
false |
Refresh even if credentials are still valid |
--log-level |
INFO |
Log verbosity: DEBUG, INFO, WARN, ERROR |
Every flag can be set via an environment variable. CLI flags always take precedence.
| Variable | Equivalent flag |
|---|---|
AWS_PROFILE |
--profile |
AWS_DEFAULT_REGION |
--region |
AWS_SHARED_CREDENTIALS_FILE |
credentials file path |
MFA_DEVICE |
--device |
MFA_STS_DURATION |
--duration |
MFA_ASSUME_ROLE |
--assume-role |
MFA_ASSUME_ROLE_SESSION_NAME |
--role-session-name |
MFA_LONG_TERM_SUFFIX |
--long-term-suffix |
MFA_SHORT_TERM_SUFFIX |
--short-term-suffix |
aws-mfa --profile stagingReads from [staging-long-term], writes to [staging].
aws-mfa --assume-role arn:aws:iam::999999999999:role/DeployRoleCalls sts:AssumeRole instead of sts:GetSessionToken. Default duration is 1 hour.
aws-mfa \
--assume-role arn:aws:iam::999999999999:role/DeployRole \
--external-id MyExternalIdaws-mfa --region us-gov-west-1
aws-mfa --region cn-north-1aws-mfa --token 123456
# or
MFA_TOKEN=123456 aws-mfa --token "$MFA_TOKEN"aws-mfa --forceaws-mfa --duration 3600 # 1 hour
aws-mfa --duration 28800 # 8 hours
aws-mfa --duration 129600 # 36 hours (GetSessionToken max)Valid ranges:
sts:GetSessionToken: 900–129600 secondssts:AssumeRole: 900–43200 seconds
You can store defaults directly in the long-term section to avoid repeating flags on every run. Precedence is always: CLI flag > environment variable > credentials file > built-in default.
[myprofile-long-term]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Optional — if omitted, auto-discovered via iam:ListMFADevices
aws_mfa_device = arn:aws:iam::123456789012:mfa/myuser
# Regional STS endpoint
region = eu-west-1
# Custom duration (seconds)
mfa_duration = 28800
# Role assumption defaults
assume_role = arn:aws:iam::999999999999:role/MyRole
assume_role_session_name = my-session
external_id = MyExternalIdComments and formatting in your credentials file are always preserved:
# This comment survives every aws-mfa run
[myprofile-long-term]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
aws_mfa_device = arn:aws:iam::123456789012:mfa/myuser
[myprofile]
aws_access_key_id = ASIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYTEMPKEY
aws_session_token = AQoXnyc4lcK4w...
aws_security_token = AQoXnyc4lcK4w...
expiration = 2026-06-18T02:00:00Z
assumed_role = falseIf aws_mfa_device is not set anywhere (credentials file, --device flag, or MFA_DEVICE env var), the tool calls iam:ListMFADevices using your long-term credentials and uses the result automatically. If your account has zero or more than one MFA device configured, it will print a clear error asking you to specify the ARN explicitly.
The default section names are [profile-long-term] (long-term) and [profile] (short-term). You can change the suffixes:
# Use [myprofile-lt] and [myprofile-st]
aws-mfa --profile myprofile --long-term-suffix lt --short-term-suffix st
# Use [myprofile] and [myprofile-temp] (no long-term suffix)
aws-mfa --profile myprofile --long-term-suffix none --short-term-suffix tempReleases are built with GoReleaser. A tagged push produces binaries for Linux, macOS, and Windows (amd64 + arm64) attached to the GitHub Release.
# Local snapshot build
goreleaser build --snapshot --cleanMIT