What's in this article
What exactly is a long-term credential
A long-term credential is any secret — a key, a password, a token — that does not expire on its own and stays valid indefinitely until someone manually revokes it. You create it once, copy it somewhere, and unless you actively go back and rotate or delete it, it just keeps working. Forever.
That is the problem in a single sentence. The credential has no expiry clock. Which means if it leaks — into a GitHub repo, a Slack message, an environment variable in a Docker image, a CI/CD log — whoever finds it has access to your cloud environment for as long as they want, until you notice and react.
Compare that to a short-lived credential — a role-assumed session token, for example — which expires in 15 minutes to a few hours. If that leaks, the attacker has a very narrow window. In most cases, by the time they try to use it, it is already expired. That asymmetry is the entire argument for roles over keys.
02AWS IAM users — the original sin
AWS IAM users are the most well-known example of this pattern. An IAM user is a permanent identity in your AWS account. You attach policies to it, and then you generate access keys — an Access Key ID and a Secret Access Key. That pair works forever, from anywhere, with no MFA required by default, as long as the policy attached to the user allows it.
Here is the lifecycle that plays out in most teams:
- Developer needs to run something against AWS from a CI/CD pipeline or their local machine.
- Someone creates an IAM user called
deploy-userorjenkins-svc. - Keys are generated and pasted into a GitHub Actions secret, a
.envfile, or a config on a server. - The person who created the user leaves the team. Nobody thinks about the key again.
- Eight months later, the key is still active, possibly with a lot of permissions, sitting in a repo that was accidentally made public last quarter.
The other problem with IAM users is that they are account-scoped identities. A key for an IAM user in your production account has no context about what service is using it, what environment it belongs to, or what it is supposed to be doing. A compromised key is just... a key. The blast radius is whatever the attached policy allows.
What long-term AWS credentials actually look like
This is what an exposed AWS credential looks like in the wild:
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Keys starting with AKIA are long-term IAM user keys. Keys starting with ASIA are
short-lived STS session tokens — those are the ones you want. If you grep your codebase for
AKIA and find results, stop reading and go fix that first.
GCP service account keys — a JSON file of unlimited power
GCP handles human identity differently from AWS — there is no concept of a standalone GCP user that exists purely inside the project. Human users authenticate via Google accounts or Workspace identities. But for workloads and automation, GCP has service accounts. And service accounts have a feature that is the source of enormous pain: downloadable JSON keys.
When you create a service account key in the GCP Console, you get a JSON file that looks like this:
{
"type": "service_account",
"project_id": "my-prod-project",
"private_key_id": "abc123...",
"private_key": "-----BEGIN RSA PRIVATE KEY-----\n...",
"client_email": "my-sa@my-prod-project.iam.gserviceaccount.com",
"client_id": "123456789",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token"
}
That JSON file is a portable, complete credential. It does not expire. It works from any machine, anywhere in the world. It requires no network access to GCP to validate — the private key is inside the file itself. And the service account it belongs to often has project-level Editor or Owner permissions, because that was the easiest way to make the CI pipeline work at the time.
What makes service account keys particularly dangerous compared to AWS IAM keys:
- No automatic expiry. AWS IAM user keys at least surface in the credential report with a "last used" date. GCP service account keys have a maximum expiry you can configure (up to 10 years by default, now capped at 1 year for new keys), but teams rarely set short windows.
- Fully self-contained. The JSON file contains everything needed to authenticate. There is no callback to GCP at the moment of authentication the way STS tokens work in AWS.
- Easy to over-share. Developers email the JSON file to a colleague. It ends up in a shared drive, a Confluence page, or a Notion doc. Once a file is shared, you cannot un-share the credential it contains.
- Hard to track usage. Identifying which workloads are using a specific service account key requires digging through audit logs. Many teams simply do not know what they have active.
Azure — no IAM users, but still has the same traps
Azure is worth discussing separately because it gets this part architecturally right in one specific way: there is no Azure equivalent of an AWS IAM user or a GCP service account key for human access. Humans authenticate via Entra ID (formerly Azure Active Directory). There is no "create a human user with an API key" option in the way AWS or GCP offer it. That is genuinely better.
But Azure absolutely still has long-term credential risks. They just show up in different places, and teams often do not think of them as "credentials" because they do not look like keys:
Storage account keys
Every Azure storage account comes with two 512-bit access keys that grant full control over the entire storage account — read, write, delete, everything. They do not expire. Many applications that integrate with Azure Blob Storage or Azure Files are built using these keys, pasted into connection strings, checked into app configuration, and promptly forgotten.
DefaultEndpointsProtocol=https;AccountName=mystorageaccount;
AccountKey=dGhpcyBpcyBub3QgYSByZWFsIGtleWJ1dCBpdCBsb29rcyBleGFjdGx5IGxpa2Ugb25l==;
EndpointSuffix=core.windows.net
App registration client secrets
When you create an App Registration in Entra ID to allow a service or application to authenticate, you generate a client secret. This secret can be configured to expire, but the default is often one or two years — and when it eventually expires, the rushed fix is usually to generate a new secret with an even longer expiry and paste it back into the configuration. The cycle repeats.
SAS tokens with no expiry
Shared Access Signatures (SAS) are meant to be short-lived, scoped tokens for delegated access to Azure Storage. In practice, teams generate account-level SAS tokens with expiry dates set years in the future, share them in URLs embedded in documentation or application configs, and then cannot revoke them without rotating the underlying storage account key — which breaks everything else that depends on it.
Discount code for FinOpsX event and FinOps certifications — LEARNCLOUDX26 | LEARNCLOUD
When this goes wrong in the real world
This is not a theoretical risk. Long-term credentials have been at the centre of some of the most significant cloud security incidents in recent years.
Capital One (2019) — $80 million fine
A misconfigured WAF allowed an attacker to perform a Server-Side Request Forgery (SSRF) attack and query the EC2 instance metadata service. The metadata service returned the IAM role credentials attached to the instance — which in this case had excessive S3 permissions. The attacker used those credentials to exfiltrate over 100 million customer records. The IAM role itself was the right approach; the problem was the permissions were far too broad. The lesson: even short-lived credentials cause damage if over-permissioned. Watch a detailed technical video about it here.
Tesla (2018) — cryptojacking via exposed Kubernetes dashboard
Tesla's Kubernetes admin console was publicly accessible without authentication. An attacker found it, accessed the cluster, and discovered AWS credentials stored as environment variables in the pods. Those credentials were used to spin up EC2 instances for cryptocurrency mining. The credentials were AWS access keys — long-term, static, sitting in plaintext environment variables inside containers. The fix would have been IAM roles for the EC2 instances running the Kubernetes nodes.
Codecov (2021) — supply chain attack via CI credential exposure
Attackers compromised Codecov's Bash uploader script and modified it to exfiltrate environment variables from CI/CD pipelines that used it. Hundreds of companies had their CI secrets stolen — including AWS access keys, GCP service account credentials, and various API tokens. Because these were long-term credentials, attackers had persistent access to downstream victims' environments even after Codecov disclosed the breach. Short-lived credentials would have expired before most victims even knew about the incident.
The fix: roles and short-lived credentials
Every major cloud has a well-supported, production-ready answer to this problem. The underlying concept is the same: instead of a static key that represents a permanent identity, you assume a role at runtime. The cloud issues a short-lived token — valid for minutes to hours — scoped to exactly what that workload needs. When the token expires, it is gone. There is nothing to leak that remains useful for long.
AWS: IAM Roles
Attach an IAM Role to your EC2 instance, Lambda function, ECS task, or EKS pod. The workload calls the metadata service and gets short-lived STS credentials automatically. No keys required. For humans, use IAM Identity Center (SSO). For GitHub Actions, use OIDC federation — the pipeline assumes a role directly without any stored secret.
GCP: Workload Identity Federation
Instead of downloading a service account JSON key, configure Workload Identity Federation so your workload (a GitHub Actions runner, a Kubernetes pod, an external system) can impersonate a service account by exchanging a short-lived OIDC token. No file to download, no key to rotate, nothing to leak.
Azure: Managed Identities
Assign a System-assigned or User-assigned Managed Identity to your VM, App Service, Function App, or AKS workload. Azure handles credential issuance and rotation entirely. The application calls the local metadata endpoint and gets a token. There is no secret to manage, store, or rotate. For external workloads, use Federated Identity Credentials on App Registrations.
For human access, the answer across all three clouds is federated identity — your corporate identity provider (Google Workspace, Entra ID, Okta, or similar) issues a short-lived assertion that the cloud exchanges for a scoped session. AWS IAM Identity Center, GCP Workforce Identity Federation, and Azure Entra ID all support this. Humans should never have permanent cloud-native user accounts with static keys.
How short-lived the tokens actually are
For reference on what "short-lived" means in practice:
- AWS STS session tokens: 15 minutes to 12 hours (1 hour default for role assumption)
- GCP access tokens via Workload Identity: 1 hour, auto-refreshed by the platform
- Azure Managed Identity tokens: 24 hours maximum, refreshed automatically well before expiry
- GitHub Actions OIDC tokens: Valid for a single job run, then gone
A credential that expires in an hour has a fundamentally different security profile than one that was created three years ago and never rotated. The former requires the attacker to use it immediately, in a narrow window, with full operational capability on their side. The latter just sits there, patiently waiting to be found.
07What to do right now
If you are managing cloud environments today and you know long-term credentials exist somewhere in your setup, here is a pragmatic starting point — not a full remediation programme, just the things that matter most immediately.
Audit what you have
On AWS, pull the IAM Credential Report (available in the console or via
aws iam generate-credential-report).
It shows every IAM user, when their access keys were last used, and whether keys exist that have
never been used. Any key not used in 90 days should be disabled. Any key not used in 180 days
should be deleted.
On GCP, use the Security Command Center or run a query in Asset Inventory for service account keys
older than 90 days. GCP now has an Organisation Policy constraint
(iam.disableServiceAccountKeyCreation) that you can enforce to prevent new keys from
being created entirely.
On Azure, review App Registration secrets and storage account keys in Entra ID and the Azure Portal. Microsoft Defender for Cloud will flag secrets nearing expiry and credentials with excessive permissions — if you are not using it, turn it on.
Stop creating new ones
The fastest way to reduce your exposure is to stop the supply. In AWS, use Service Control Policies
to deny iam:CreateAccessKey for IAM users in environments where roles are available. In mature enteprises, IAM user creation is treated as an exception, and it involves a long difficult approval process.
In GCP, enforce the iam.disableServiceAccountKeyCreation org policy. In Azure,
restrict who can create App Registration secrets via Entra ID roles.
Check out this detailed video about building a strong IAM model for your enterprise.
Migrate workloads to roles
Pick the highest-risk credentials first — the ones with the broadest permissions, used in CI/CD pipelines, or stored in places you are not fully confident about. Replace them with the role-based equivalent for their platform. For most common use cases — GitHub Actions, Lambda, EC2, App Service, GKE — the migration is well-documented and takes hours, not weeks.
The core principle to take away
Every credential is a liability. A long-term credential is an indefinite liability. The goal is not to manage credentials better — it is to eliminate the need for them entirely wherever the platform gives you a role-based alternative. In 2026, across AWS, GCP, and Azure, that alternative exists for virtually every workload pattern you will encounter. There is no longer a good technical reason to be generating static keys for machines or for people.
The question to ask whenever a key is being created: what role can replace this? If the answer is "I'm not sure," that is the thing worth figuring out. The key can wait.