Most organisations have a rotation policy for secrets, but few can say how old their live credentials actually are or what share of them a rotation process really covers. This guide defines the two measures that answer those questions, secret age and rotation coverage, and shows where the timestamps come from in AWS, Azure, Google Cloud, HashiCorp Vault and Microsoft Entra ID. It covers the queries to run, the traps in each platform's date fields, a scorecard format, and how to set targets that drive work rather than decorate a dashboard. It is for secrets management, platform and identity engineers and for the security leads who report on them. Measuring secrets that have leaked outside approved stores is a different problem, covered in the Secret Sprawl Measurement Guide.
Key takeaways
- Age is measured from the last value change, not from when the secret object was created or last edited. Every platform exposes a different field for this, and some fields move for reasons that have nothing to do with rotation.
- Rotation coverage has three levels: scheduled (a rotation is configured), succeeding (the last scheduled rotation completed on time) and effective (consumers moved to the new value and the old one stopped working). Report all three.
- The denominator decides the truth of the metric. Coverage of secrets inside a manager says nothing about IAM user keys, app registration passwords or service account keys outside it.
- Report distributions, not averages: the median, the 90th percentile and the count over threshold show the backlog; a mean hides the handful of ten-year-old keys that matter most.
- Targets come from risk tiers: shorter limits for credentials that reach production data or admin rights, and a separate track that replaces long-lived secrets with federation instead of rotating them faster.
Definitions that hold up in a review
Write the definitions down before collecting any numbers, because each platform invites a slightly different reading.
| Metric | Definition | Notes |
|---|---|---|
| Secret age | Days since the current credential value was issued or last changed | For keys and passwords issued by a provider, the issue date of the active key; for stored secrets, the creation date of the current version |
| Age over threshold | Count and share of active credentials older than the tier limit | The headline backlog figure, assignable by owner |
| Scheduled rotation coverage | Active credentials with an automated rotation or expiry configured, divided by all active credentials in scope | Measures intent, not outcome |
| Rotation success rate | Scheduled rotations that completed by their due date, divided by rotations due in the period | Failed or abandoned rotations count as misses |
| Effective rotation coverage | Credentials whose previous value is confirmed revoked within the overlap window after rotation | The level that matters after a leak |
| Expiry coverage | Credentials with a hard expiry date set, divided by all in scope | Relevant where the platform supports expiry but not rotation, such as app registration passwords |
| Unowned share | Credentials with no named team or service owner | Unowned credentials are rarely rotated |
Two choices need agreement up front. First, whether disabled or inactive credentials stay in the denominator (our recommendation: exclude them from coverage but report them separately as clean-up debt, because a disabled key can be re-enabled). Second, how to treat secrets that are rotated by a managed service on your behalf. Count them as covered only where the platform reports a completed rotation date you can read.
Where the timestamps live
Each platform records age differently. The table lists the fields to read and the trap in each.
| Platform | Where to read age and rotation state | Trap |
|---|---|---|
| AWS IAM user access keys | Credential report columns access_key_1_last_rotated, access_key_2_last_rotated, access_key_1_active, access_key_1_last_used_date | The report covers only the first two keys per user and excludes service-specific credentials; use ListAccessKeys and ListServiceSpecificCredentials for completeness |
| AWS Secrets Manager | ListSecrets entries: CreatedDate, LastRotatedDate, LastChangedDate, RotationEnabled, NextRotationDate, LastAccessedDate | LastChangedDate moves when the secret is modified "in any way"; LastRotatedDate is null if the secret has never rotated |
| Azure Key Vault secrets | Current version attributes created, updated, exp, nbf, enabled | exp defaults to no expiry; older secrets may have null created values |
| Microsoft Entra app registrations and service principals | passwordCredentials with startDateTime and endDateTime (and keyCredentials for certificates) | Several passwords can be valid at once; the oldest one still valid is the one that counts |
| Google Cloud Secret Manager | Secret rotation.next_rotation_time and rotation.rotation_period; version creation time for age | Secret Manager sends a SECRET_ROTATE Pub/Sub message but does not change the value; a schedule is not proof of rotation |
| HashiCorp Vault KV version 2 | GET /<mount>/metadata/<path>: current_version and that version's created_time | Top-level updated_time also changes for metadata edits; use the current version's created_time |
AWS: IAM keys and Secrets Manager
- Credential report:
aws iam generate-credential-report, thenaws iam get-credential-reportreturns a base64 CSV. IAM generates a new report at most once every four hours and stores only the latest, so schedule the pull rather than running it ad hoc. Age is today minusaccess_key_N_last_rotatedwhereaccess_key_N_activeisTRUE;N/Ameans no key. - Secrets Manager: page through
ListSecretsand compute age fromLastRotatedDatewhere present, otherwise fromCreatedDate. A secret whoseLastChangedDatekeeps moving whileLastRotatedDatestays null is probably being updated by hand (our inference, not an AWS statement). The step-by-step query is in How to find unrotated secrets in AWS Secrets Manager. - Built-in checks: AWS Security Hub control IAM.3 uses the Config rule
access-keys-rotatedwithmaxAccessKeyAgefixed at 90 days, and IAM.8 and IAM.22 flag credentials unused for 90 and 45 days. These give a ready-made count over threshold per account, but only for IAM users.
Azure Key Vault and Entra ID
- Key Vault: list secrets per vault and read the current version's
createdandexp. Microsoft documentsexpas the time after which secret data should not be retrieved; it is optional and defaults to forever, so the share of secrets withexpset is a useful expiry coverage metric in its own right. Event Grid publishesMicrosoft.KeyVault.SecretNearExpiry30 days before expiry andMicrosoft.KeyVault.SecretExpiredon expiry, which gives a live signal to compare with the inventory. See the Azure Key Vault Security Guide. - Entra ID: read
passwordCredentialson applications and service principals through Microsoft Graph. Age is today minusstartDateTime; remaining life isendDateTimeminus today. Credentials with very long end dates are a separate finding. See How to find expiring app secrets and certificates in Entra ID.
Google Cloud and Vault
- Secret Manager: treat a configured
rotation_periodas scheduled coverage only. Success is a new version created after eachSECRET_ROTATEmessage. Google notes that scheduled rotations are skipped while one is in flight and that failed message delivery is retried for up to seven days, so compare version creation times against the schedule. - Vault KV v2:
LIST /<mount>/metadata/<path>walks the tree; for each secret, read the metadata and takeversions[current_version].created_time. Dynamic secrets engines issue leases with their own time to live and are better measured as federation adoption than as age. See the HashiCorp Vault Guide.
Building the inventory and the query pipeline
- Fix the scope: list every credential class in the estate: IAM user keys, secrets manager entries, app registration passwords and certificates, cloud service account keys, database logins, SaaS API tokens and personal access tokens used by automation. Each class becomes a row in the scorecard, even if its first value is "unknown".
- Collect read-only: run collectors under a dedicated identity with list and metadata permissions only, never permission to read secret values. Store timestamps, identifiers and owner tags, not plaintext. For Vault, the
listcapability on the metadata path is enough. - Normalise: map each record to one schema: credential ID, class, platform, environment, owner, tier, value-issued date, rotation configured (yes or no), last successful rotation, next due date, expiry date and last used date.
- Join to owners and identities: attach each credential to the service account or workload it authenticates and to a team, using tags or an NHI inventory. See NHI Inventory Reconciliation.
- Compute and snapshot: calculate the metrics daily or weekly, keep each snapshot, and report trends. Recompute history when a new credential class is added, or label the jump as new visibility.
- Validate by sampling: each month pick a few credentials reported as rotated and confirm at the issuer that the previous value no longer authenticates. This is how effective coverage is measured, and it catches rotations that created a new value while leaving the old one live.
Setting targets by risk tier
A single age limit for every secret either overloads teams or leaves the riskiest credentials too old. Tier first, then set limits. The values below are NHI Mgmt Group recommendations, not requirements from any standard; AWS's 90-day IAM.3 check is one widely used external reference point.
| Tier | Examples | Suggested maximum age | Coverage target |
|---|---|---|---|
| 1: privileged or production data | Cloud admin keys, database owner passwords, signing keys held as secrets, CI/CD deployment credentials | 30 to 90 days, or replace with federation | 100% scheduled, 95% or more succeeding |
| 2: production service-to-service | Application database logins, internal API keys, third-party API tokens | 90 to 180 days | 90% or more scheduled |
| 3: non-production and low impact | Test environment secrets, read-only monitoring tokens | Up to 365 days with expiry set | Expiry set on all |
Rotation frequency is not the only lever. NIST SP 800-57 Part 1 treats cryptoperiods as a risk decision for each key type, and the same logic applies here: where a workload can use short-lived federated credentials instead, the better target is "no long-lived secret at all", tracked as a federation adoption share. See Credential Rotation at Scale for the dependency and overlap work that makes shorter limits safe.
Decision guidance
- Small organisations: start with the cloud provider's native findings (Security Hub IAM.3, Key Vault expiry events) and one spreadsheet scorecard per quarter. Two metrics are enough at first: count over threshold for tier 1, and scheduled coverage.
- Large organisations: build a normalised store fed by collectors in every account, subscription, project and Vault namespace, with owner tags enforced at creation. Add success rate and effective coverage once scheduled coverage passes about 80% (our suggestion), because before that the denominator is still moving.
- Rotate or expire: where a platform supports expiry but not automated rotation (Entra app passwords, many SaaS tokens), measure expiry coverage and remaining life rather than forcing a rotation metric that cannot be met.
- Rotate or retire: a tier 1 credential that cannot be rotated without an outage is a candidate for redesign, not an exception to renew every year.
Where identity fits
Every secret on the scorecard authenticates a non-human identity: a service account, an app registration, a pipeline or an AI agent. Age and coverage only become actionable when each credential is joined to that identity, its owner and its permissions, so that an old key on an admin role sorts above an old key on a read-only role. The same join exposes identities holding several valid credentials at once, which doubles the exposure during and after a rotation. See the Secrets Management Guide and the Identity Security Metrics Guide.
Detection and reporting
Report monthly to engineering owners and quarterly to security leadership. The core KPIs:
- Median and 90th percentile secret age per tier, with the oldest ten credentials named.
- Count and share of active credentials over the tier limit, by owner.
- Scheduled rotation coverage per credential class.
- Rotation success rate for the period, with failures by cause.
- Effective rotation coverage from the monthly sample.
- Share of credentials with no owner, and share replaced by federation.
Alert as well as report: a RotationFailed event from Secrets Manager in CloudTrail, a Key Vault SecretExpired event, or a credential that passes its due date should open a ticket for the owner the same day.
Common failure patterns
- Measuring the wrong date: using object creation or last-modified time, so a secret renamed or retagged last week shows as fresh while its value is years old.
- Counting schedules as rotations: a rotation period configured in Google Cloud or a Lambda attached in AWS that has been failing silently for months still shows as covered.
- A denominator limited to the vault: coverage reported as 95% of secrets manager entries while IAM user keys, app passwords and SaaS tokens outside it are never counted.
- Rotation without revocation: a new value issued but the old one left valid. The Cloudflare Thanksgiving breach followed credentials that were not rotated after the Okta compromise, and the Internet Archive breach involved Zendesk keys left unrotated after an earlier exposure. An effective coverage metric would have surfaced both. See Incomplete rotation breaches.
- Averages that hide the tail: a mean age of 60 days can include a handful of tier 1 keys older than five years.
- Collectors with read access to values: the metrics pipeline itself becomes a high-value secret store.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev 5 | IA-5 Authenticator Management | Changing and refreshing authenticators, managing their lifetime |
| NIST SP 800-53 Rev 5 | SC-12 Cryptographic Key Establishment and Management | Key lifetimes where secrets are cryptographic keys |
| NIST SP 800-53 Rev 5 | CA-7 Continuous Monitoring | Ongoing metrics and reporting |
| NIST CSF 2.0 | PR.AA-01 | Identities and credentials for users, services and hardware are managed |
| NIST CSF 2.0 | GV.OV-03 | Risk management performance is evaluated and reviewed |
| MITRE ATT&CK | T1552 Unsecured Credentials, T1078.004 Cloud Accounts | Older credentials have had longer to be copied and reused |
This mapping is NHI Mgmt Group's own and indicative.
Practitioner checklist
- Write definitions for age, scheduled, succeeding and effective coverage, and agree them with owners.
- List every credential class in scope, including those outside a secrets manager.
- Read the value-change field on each platform, not object creation or last-modified time.
- Run collectors with metadata-only permissions and never store secret values.
- Join each credential to its identity, owner and privilege tier.
- Set maximum ages per tier and record them as policy.
- Report median, 90th percentile and count over threshold, not a mean.
- Sample rotated credentials monthly to prove the old value is revoked.
- Alert on failed rotations and expired secrets the same day.
- Track federation adoption alongside rotation for tier 1 workloads.
Standards and references
- NIST: SP 800-57 Part 1 Rev. 5, Recommendation for Key Management
- NIST: SP 800-53 Rev. 5
- AWS: Getting credential reports for your AWS account
- AWS: Secrets Manager SecretListEntry
- AWS: Security Hub controls for IAM
- Microsoft: About Azure Key Vault secrets
- Microsoft: Azure Key Vault as Event Grid source
- Google Cloud: Rotation schedules in Secret Manager
Related NHI Mgmt Group resources: Secret Sprawl Measurement Guide · Credential Rotation at Scale · Secrets Rotation Runbook Template · Security Metrics and KPIs Programme Guide