Your deploy pipeline shouldn't hold a cloud key that works forever.
An access key pasted into a CI variable years ago is still one of the easiest ways into a cloud account. We move pipelines, containers and servers to workload identity federation, where each job gets a short-lived token tied to what it is, and then we delete the old key.
CI pipeline jobGitHub Actions · GitLab CIKubernetes podAKS · EKS · GKEOn-premises servercertificate from your CA
02 · Workload identity
CI OIDC issuerrepo · branch · environment claimsProjected SA tokenone service account per appSPIRE servernode and workload attestation
03 · Trust and exchange
Federated credentialEntra Workload ID · AWS IAM roleIAM Roles AnywhereX.509 for temporary AWS creds
04 · Resources
Cloud APIsAzure · AWS · Google CloudPeer servicesmTLS across clusters
TerraformMicrosoft Entra IDHashiCorp Vault
in short·15-second read
Machine and workload identity gives software its own verifiable identity.
Pipelines, containers and servers can then authenticate without stored passwords or keys. Cohash replaces static cloud credentials with federation. We use GitHub Actions OIDC for AWS, Azure and Google Cloud, and Microsoft Entra Workload ID for Kubernetes. SPIFFE and SPIRE cover services that span clouds, and AWS IAM Roles Anywhere covers servers outside AWS.
you probably need this if
Deploy pipelines hold cloud access keys that never expire.
Kubernetes workloads in one namespace share a single service account.
Entra app registrations still authenticate with client secrets.
From the first call to a setup your own team can run.
Here's what usually prompts a call about your Machine & Workload Identity, what we do about it and in which order, and where you end up.
01 · where it hurts02 · what we change03 · how it runs04 · where you end up
incident board · machine & workload identity3 open
openDeploy pipelines hold cloud access keys that never expire.
recurringKubernetes workloads in one namespace share a single service account.
blockedEntra app registrations still authenticate with client secrets.
exposure
CI/CD federationKubernetes workload identitySPIFFE and SPIREServers outside the cloudStatic credential retirementLeast-privilege role design
startrehearsehandover
Inventory static credentials
Pick a pattern per workload
Migrate and verify
Add guardrails
we are here
ready to hand over
Production deploys get short-lived credentials only from the protected environment, the…
Pods get tokens tied to their own service account, no client secret sits in the cluster…
The servers now get temporary AWS credentials based on their machine certificate.
Static credential inventory mapped to workloads
chapter 01 · where it hurts
Why teams reach out
Deploy pipelines hold cloud access keys that never expire.
Kubernetes workloads in one namespace share a single service account.
Entra app registrations still authenticate with client secrets.
chapter 02 · what we change
How we solve it
CI/CD federation
Kubernetes workload identity
SPIFFE and SPIRE
Servers outside the cloud
Static credential retirement
Least-privilege role design
chapter 03 · how it runs
Our approach
Inventory static credentials
Pick a pattern per workload
Migrate and verify
Add guardrails
chapter 04 · where you end up
What changes
Production deploys get short-lived credentials only from the protected environment, the admin IAM user is gone, and a fork or feature branch can't assume the prod role.
Pods get tokens tied to their own service account, no client secret sits in the cluster, and the subscription-wide grant is gone.
The servers now get temporary AWS credentials based on their machine certificate.
Static credential inventory mapped to workloads
what we do
Where we spend the hours
6 areas of work, each one done by an engineer who has run it in production.
Pipelines trade their OIDC token for cloud credentials scoped to one repository, branch or environment. We write the trust conditions tightly enough that a fork or a feature branch can't deploy to production.
Pods on AKS use Microsoft Entra Workload ID through projected service account tokens, and we apply the equivalent native pattern on other clusters. One Kubernetes service account per workload, never one shared per namespace.
Services that talk mTLS across clusters and clouds get SPIFFE identities from SPIRE, which attests both the node and the workload first. Both projects graduated from the CNCF in 2022. Entra can federate with SPIFFE identities.
IAM Roles Anywhere trades an X.509 certificate from your own CA for temporary AWS credentials. Entra federated identity credentials do the same thing for tokens from an outside issuer. No long-lived key either way.
Entra app registrations with client secrets. AWS access keys nobody uses. Google service account keys. We find them, match each to its workload, and retire them once federation is working.
Roles shaped from the API calls each workload actually makes in cloud audit logs, with permission boundaries or deny policies that stop privileges creeping back.
capability 01 / 06in production
CI/CD federation
Pipelines trade their OIDC token for cloud credentials scoped to one repository, branch or environment. We write the trust conditions tightly enough that a fork or a feature branch can't deploy to production.
Pods on AKS use Microsoft Entra Workload ID through projected service account tokens, and we apply the equivalent native pattern on other clusters. One Kubernetes service account per workload, never one shared per namespace.
Services that talk mTLS across clusters and clouds get SPIFFE identities from SPIRE, which attests both the node and the workload first. Both projects graduated from the CNCF in 2022. Entra can federate with SPIFFE identities.
IAM Roles Anywhere trades an X.509 certificate from your own CA for temporary AWS credentials. Entra federated identity credentials do the same thing for tokens from an outside issuer. No long-lived key either way.
Entra app registrations with client secrets. AWS access keys nobody uses. Google service account keys. We find them, match each to its workload, and retire them once federation is working.
Roles shaped from the API calls each workload actually makes in cloud audit logs, with permission boundaries or deny policies that stop privileges creeping back.
We pieced these together from Machine & Workload Identity situations that come up a lot. They're patterns. You won't find a specific client in them.
case file 01
GitHub Actions deploying to AWS with an admin access key per environment
composite · not a client story
Beforethe situation
Every repository keeps AWS access keys for dev, staging and prod as secrets. The prod key belongs to an IAM user with AdministratorAccess that someone created years ago. Any workflow on any branch can read it.
What we dothe work
Add GitHub as an IAM OIDC identity provider, then create one role per repository and environment whose trust policy matches the sub claim on both. Switch workflows to configure-aws-credentials with role-to-assume, check CloudTrail for the old key's last use, then deactivate it.
Afterwhere you end up
Production deploys get short-lived credentials only from the protected environment, the admin IAM user is gone, and a fork or feature branch can't assume the prod role.
case file 02
AKS pods sharing one service principal secret with subscription Contributor
composite · not a client story
Beforethe situation
Every pod in an AKS namespace mounts the same client secret from a Kubernetes secret. The service principal behind it has Contributor on the whole subscription, and rotation happens when someone remembers.
What we dothe work
Turn on the cluster's OIDC issuer and workload identity, create a user-assigned managed identity per workload with a federated credential bound to its Kubernetes service account, and grant each only the Key Vault or storage scope it uses. Delete the shared secret after the last pod moves.
Afterwhere you end up
Pods get tokens tied to their own service account, no client secret sits in the cluster, and the subscription-wide grant is gone.
case file 03
Data center servers uploading to S3 with a key in a config file
composite · not a client story
Beforethe situation
Linux servers in a colocation facility push files to S3 with an IAM user's access key sitting in a config file. Meanwhile the internal PKI already issues machine certificates to those same servers.
What we dothe work
Register the internal CA as an IAM Roles Anywhere trust anchor, create a profile and role scoped to the bucket prefix each server writes, and wire the credential helper into the SDK's credential process. Retire the IAM user once CloudTrail shows no further use.
Afterwhere you end up
The servers now get temporary AWS credentials based on their machine certificate. When one is decommissioned, you revoke the certificate. No one goes looking for an access key in a config file.
How a workload trades its own identity for a short-lived cloud token
Each CI job, pod and server gets a token or certificate saying what it is. It trades that for short-lived cloud credentials through a federated trust. Where workloads span clouds, SPIRE handles service-to-service mTLS.
How a workload trades its own identity for a short-lived cloud tokenEach CI job, pod and server gets a token or certificate saying what it is. It trades that for short-lived cloud credentials through a federated trust. Where workloads span clouds, SPIRE handles service-to-service mTLS.01 · WORKLOADS02 · WORKLOAD IDENTITY03 · TRUST AND EXCHANGE04 · RESOURCESrequest tokenprojected tokenworkload attestationOIDC token exchangetoken exchangeX.509 certificateshort-lived tokentemporary credentialsSVID for mTLSCI pipeline jobGitHub Actions · GitLab CIKubernetes podAKS · EKS · GKEOn-premises servercertificate from your CACI OIDC issuerrepo · branch ·environment claimsProjected SA tokenone service account perappSPIRE servernode and workloadattestationExFederatedcredentialEntra Workload ID ·AWS IAM roleIAM Roles AnywhereX.509 for temporary AWScredsCloud APIsAzure · AWS · Google CloudPeer servicesmTLS across clusters
01 · Workloads
CI pipeline jobGitHub Actions · GitLab CI → CI OIDC issuer · request token
Kubernetes podAKS · EKS · GKE → Projected SA token · projected token→ SPIRE server · workload attestation
On-premises servercertificate from your CA → IAM Roles Anywhere · X.509 certificate
IAM Roles Anywhere → Cloud APIs: temporary credentials
SPIRE server → Peer services: SVID for mTLS
how it runs
A typical engagement
We don't start a step until the last one has produced something you can check.
step 01
Inventory static credentials
Cloud access keys, client secrets and service account keys, each matched to the workload that uses it.
step 02
Pick a pattern per workload
Federation for CI and cloud-native workloads, SPIFFE for mesh traffic, and vault-issued credentials for the rest.
step 03
Migrate and verify
New trust configured, workload switched, old key disabled and watched before it's deleted.
step 04
Add guardrails
New long-lived keys are blocked by policy. If you need an exception, it's tracked and gets an end date.
you keep
What you're left with
We write these for your Machine & Workload Identity setup, and they're working documents. Your team should be able to keep things running from them long after we've gone.
Some clients want a project team, some want a couple of engineers on theirs. Either works. Onsite, nearshore or offshore, with handovers across time zones that keep things moving.
Put these to anyone bidding on your Machine & Workload Identity work. The ones who've done it answer differently from the ones who've read about it.
Q1
Which exact claims will our federated trust policies match on?
A trust scoped to an entire GitHub organization, or to a wildcard subject, lets any repository or branch assume the role. So ask to see each trust condition before it goes live.
Q2
How will you find which workload uses each static key before you disable it?
Keys get copied into scripts, pipelines and people's laptops. CloudTrail, Entra sign-in logs and Google Cloud audit logs show where each key is actually called from. That list decides the migration order.
Q3
Which workloads will stay on vaulted secrets, and why?
SaaS APIs and older databases often can't federate. A partner should name those and plan rotation for them instead of promising every credential disappears.
Q4
How will you stop new long-lived keys after the migration?
Leave out the guardrail and the next project creates an access key on day one. You can block or flag new keys with organization policies, SCPs or Azure Policy, and track the exceptions.
01Can we get rid of every static credential?
Not every one. Many SaaS APIs and older databases only accept API keys or passwords. Those move into a vault with rotation, which our secrets and certificate page covers. CI-to-cloud and cloud-to-cloud access can almost always use federation.
02Do we need SPIRE if we're on a single cloud?
Usually not. The cloud's own workload identity handles single-cloud estates well. SPIRE earns its operating cost when you need one identity scheme across several clouds, on-premises clusters, or a service mesh with mTLS everywhere.
03What breaks when we turn off an old access key?
Whatever still uses it, which is why we disable before we delete. We check cloud audit logs for the key's last use, disable it, wait through at least one full business cycle including month-end jobs, then delete it.
04How do we stop a pull request from a fork deploying to production?
With narrow trust conditions on the federated credential. Match the repository, the branch or environment, and ideally the workflow, rather than the organization alone. We review every trust policy for wildcards before go-live.
05How long does a move to workload identity federation take?
It depends on how many static credentials you have and how many workloads share each one. A single pipeline can move in a day. A key shared by many consumers takes longer, because every one has to switch before the key is disabled. We size it after the credential inventory.
related
Often paired with
Projects on Machine & Workload Identity usually end up touching one of these, so they're worth a look.