Back to blog
AWSBest PracticesCloud SecurityIdentity & AccessOperations & Compliance

Expired IAM Server Certificate: Detection, Fix, and Prevention

Learn how to detect, remediate, and prevent expired IAM server certificates in AWS, with CLI steps, ACM migration tips, and policy-as-code gates.

TL;DR

This check flags IAM server certificates that have already expired or will expire within 30 days. Expired certs break TLS on whatever they back, which means broken HTTPS endpoints or failed load balancer handshakes. Rotate the certificate before it lapses and switch new workloads to ACM where possible.

IAM server certificates are the older way to store X.509 certificates in AWS, predating AWS Certificate Manager. They still hang around in plenty of accounts, often attached to classic Elastic Load Balancers, CloudFront distributions in specific edge cases, or uploaded for use with services that do not support ACM. When one of these certificates expires, anything relying on it for TLS stops working cleanly, and you usually find out from a flood of client errors rather than a calm dashboard alert.

The account_expiredcerts check catches this before it bites. It looks at every server certificate in your IAM certificate store and raises a finding when the expiration date has passed or is less than 30 days away.


What this check detects

The check enumerates server certificates stored in IAM and inspects the Expiration field on each one. It returns a finding in two situations:

  • The certificate has already expired (expiration date is in the past).
  • The certificate will expire within the next 30 days.

These are certificates managed through IAM, not ACM. You can list them yourself with a single call:

aws iam list-server-certificates \
  --query 'ServerCertificateMetadataList[*].[ServerCertificateName,Expiration]' \
  --output table

Note: IAM server certificates and ACM certificates are two completely separate stores. ACM handles its own renewals automatically for certificates it issues. IAM does not renew anything for you, which is exactly why expired IAM certs are so common in long-lived accounts.


Why it matters

A TLS certificate is a hard deadline. The moment it expires, the trust chain breaks, and there is no grace period built into the protocol. The impact depends on what the certificate is attached to, but none of the outcomes are good.

Broken HTTPS for end users

If the certificate backs a public endpoint, browsers throw a full-page NET::ERR_CERT_DATE_INVALID warning. Users cannot click past it on sites using HSTS, so the page is effectively down. API clients with strict certificate validation simply fail the connection. This is the classic outage that takes down checkout flows, login pages, and partner integrations all at once.

Failed load balancer and service handshakes

Classic ELBs that reference an expired IAM certificate will reject TLS connections at the listener. Internal services that pin or validate the certificate stop talking to each other. Because these failures cascade, a single expired cert can look like a much larger infrastructure problem during the first frantic minutes of an incident.

Silent risk in forgotten corners

The dangerous case is the certificate nobody remembers uploading. It might back a low-traffic admin panel or a legacy callback URL that only fires once a month. When it expires, the failure is intermittent and confusing, and the on-call engineer burns hours tracing it back to an IAM store they did not know existed.

Warning: Expired certificates are not just an availability problem. Some teams respond to cert errors by telling clients to bypass validation as a quick fix. That habit trains users and services to ignore certificate warnings, which opens the door to man-in-the-middle attacks. Fix the cert, never the validation.


How to fix it

The remediation is to replace the expired or expiring certificate. The cleanest long-term move is to migrate to ACM, but if you must stay on IAM, you upload a fresh certificate and repoint whatever consumes it.

Step 1: Identify what uses the certificate

Before you touch anything, find out where the certificate is referenced. Check classic load balancers first:

aws elb describe-load-balancers \
  --query 'LoadBalancerDescriptions[?ListenerDescriptions[?Listener.SSLCertificateId!=`null`]].[LoadBalancerName]' \
  --output text

Then inspect the listener that points at your certificate ARN so you know exactly which port to update later.

Step 2: Get a new certificate

Obtain a renewed certificate from your CA, or generate a new one. You will need three files: the certificate body, the private key, and the certificate chain.

Step 3: Upload the new certificate to IAM

Upload it under a new name. Do not try to overwrite the old one in place, because anything still pointing at the old ARN needs a window to cut over.

aws iam upload-server-certificate \
  --server-certificate-name my-app-cert-2025 \
  --certificate-body file://certificate.pem \
  --private-key file://private-key.pem \
  --certificate-chain file://chain.pem

This returns the metadata for the new certificate, including its ARN. Copy that ARN.

Step 4: Repoint the consuming resource

Update the load balancer listener (or other consumer) to use the new ARN:

aws elb set-load-balancer-listener-ssl-certificate \
  --load-balancer-name my-app-elb \
  --load-balancer-port 443 \
  --ssl-certificate-id arn:aws:iam::123456789012:server-certificate/my-app-cert-2025

Verify the endpoint serves the new certificate before moving on:

echo | openssl s_client -connect my-app.example.com:443 2>/dev/null \
  | openssl x509 -noout -dates

Step 5: Delete the old certificate

Danger: Only delete the old certificate after you have confirmed that nothing references it. Deleting a certificate still attached to an active listener will break TLS for that endpoint immediately, and there is no undo. Run the verification in Step 4 first.

aws iam delete-server-certificate \
  --server-certificate-name my-app-cert-old

The better path: migrate to ACM

If the service supporting your workload accepts ACM certificates (Application Load Balancers, Network Load Balancers, CloudFront, API Gateway), request a certificate there and let AWS handle renewal for you.

aws acm request-certificate \
  --domain-name my-app.example.com \
  --validation-method DNS \
  --subject-alternative-names "*.my-app.example.com"

ACM auto-renews certificates it issues as long as the DNS validation record stays in place, which removes this entire class of finding from your account over time.

Tip: Classic ELBs are the most common reason teams stay stuck on IAM certificates. If you are already touching the load balancer to swap certs, consider migrating that endpoint to an Application Load Balancer with ACM in the same change window. You solve the immediate problem and the recurring one at once.


How to prevent it from happening again

Expiry is predictable, so the fix is to make the deadline visible long before it arrives and to stop new IAM certificates from being added without an owner.

Monitor expiration continuously

Run a scheduled check that lists IAM certificates and alerts when any is within your threshold. A simple Lambda on a daily EventBridge schedule works:

aws iam list-server-certificates \
  --query "ServerCertificateMetadataList[?Expiration<='$(date -d '+30 days' --iso-8601)'].ServerCertificateName" \
  --output text

Pipe any non-empty result to SNS or your incident tool. Thirty days gives you room to renew calmly instead of at 2 a.m.

Gate new certificates in CI/CD

If your infrastructure is defined in Terraform, treat aws_iam_server_certificate as a resource that needs justification. Add a policy check that flags its use and nudges teams toward aws_acm_certificate instead.

An example OPA / Conftest rule against a Terraform plan:

{
  "deny": [
    "IAM server certificates are not allowed. Use aws_acm_certificate instead."
  ]
}

And the Rego behind it:

package main

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_iam_server_certificate"
  msg := "IAM server certificates are not allowed. Use aws_acm_certificate instead."
}

Tag certificates with an owner

Every certificate should have a clear owner and a renewal process documented somewhere a human will actually look. Orphaned certificates are the ones that expire, because no one is watching their calendar.

Tip: Lensix runs account_expiredcerts on a schedule across all your accounts, so you get the 30-day warning without building and maintaining the Lambda yourself. Wire the finding into your alerting and you have continuous coverage out of the box.


Best practices

  • Prefer ACM over IAM for everything that supports it. Auto-renewal is the single biggest reduction in operational risk you can make here.
  • Keep a 30-day or longer warning threshold. Shorter windows leave no room for CA delays, change freezes, or validation hiccups.
  • Use DNS validation for ACM certificates. Email validation requires manual action on each renewal, which reintroduces the chance to miss a deadline.
  • Inventory before you delete. Always confirm a certificate is unreferenced before removing it from IAM. Track usage across ELBs, CloudFront, and any custom integrations.
  • Audit your IAM certificate store regularly. Long-lived accounts accumulate certificates from people who have since left. Periodically list them and decommission the dead ones.
  • Never disable certificate validation as a workaround. A broken cert is an availability incident. Bypassing validation turns it into a security incident.

Certificate expiry is one of the few outages you can predict to the second. With monitoring on the IAM store, a clean renewal runbook, and a steady migration toward ACM, this check should fire less and less often until it stops being a concern at all.