NIST SP 800-53 Rev. 5 · Access Control
AC-2 on AWS
Residual access after termination — a published SQL (Athena) + Lambda query you can run against Amazon Athena over IAM inventory + supplied HR feed, with the evidence it needs, the settings you decide, and what a result does and does not establish.
Why this matters
A departed employee whose access was never switched off
When someone leaves an organization, their accounts are supposed to be disabled. In practice the human resources record and the computer system are often maintained by different people using different tools, and accounts survive departures. Those accounts are a recurring feature of real breach reports.
Why this check earns its place. It requires joining two sources that do not normally talk to each other — the staff record and the directory. That join is the work most small organizations cannot do for themselves.
- What it reads
- The organization's staff-departure records joined to its user directory.
- What it reports
- Accounts still active after the person's recorded departure date.
See related public disclosures on the Signals page →
What this finds
Identity still enabled after the recorded termination date, with no unexpired access extension.
The requirement
The organization must disable or remove information system accounts when an individual is terminated, transferred, or otherwise no longer requires access, within an organization-defined time period.
NIST SP 800-53 Rev. 5 — AC-2 (Account Management).
NIST states the objective. The authoritative lifecycle source, the subject-matching identifier, the permitted grace period, and the exception workflow are all organization-defined — not NIST-prescribed values.
How this maps to the CISA performance goals
The Cross-Sector Cybersecurity Performance Goals are a voluntary baseline published by CISA — the U.S. federal agency for critical-infrastructure cybersecurity — written for organizations too small to employ security staff. This control speaks to:
- 3.D — Revoke credentials for departing staff
The goal is revoking access when someone leaves; the pack finds access that outlived a termination.
See the full coverage map, including the goals no query can answer →
Evidence this query needs
HR termination events hr_termination_events.csv
Establishes when an individual's employment ended so directory state can be compared against it. Without this file, an enabled account is just an enabled account — there is nothing to compare it to.
Minimum fields: user_principal_name, employment_status, effective_date
A UPN, a status, and a date are enough to run the check. Everything else improves the reviewer's ability to dismiss a false positive without going back to HR.
The query
Published as ac-2-residual-access.aws.sql. Reads iam_user_inventory (Config export), hr_termination_events (supplied). Requires: athena:StartQueryExecution; glue:GetTable; s3:GetObject.
Before this returns anything,
iam_user_inventory (Config export) has to be reaching Amazon Athena over IAM inventory + supplied HR feed, and hr_termination_events (supplied) has to be landed there. If not, the query reports nothing found — which looks
exactly like nothing wrong. What has to be switched on first →
-- AC-2 -- IAM users still active after an HR termination event
-- Requires the hr_termination_events contract registered as an Athena table.
SELECT
i.user_name,
i.arn,
h.effective_date,
h.retain_access_until,
date_diff('day', h.effective_date, current_date) AS days_since_termination,
'AWS IAM' AS source_system
FROM iam_user_inventory i
JOIN hr_termination_events h
ON lower(i.user_name) = lower(h.user_principal_name)
WHERE i.user_status = 'Active'
AND h.employment_status = 'terminated'
AND h.effective_date < current_date
-- approved extension suppresses until it lapses
AND (h.retain_access_until IS NULL OR h.retain_access_until < current_date)
ORDER BY days_since_termination DESC;
Open this pack on AWS to adjust the settings and download it →
The interactive version opens on AWS — the platform on this page — even if your environment profile does not list it.
Settings you decide
NIST states the objective and leaves these to your organization. The interactive version of this pack applies them to the query for you.
- Disablement grace period (days) (default 0)
- Accounts are only reported once this many days have passed since the effective termination date.
- Limit to specific accounts (optional)
- Paste IAM user names, one per line, to scope the query to a defined population.
Running it continuously
EventBridge Scheduler → Lambda → Security Hub
Schedule: EventBridge Scheduler, rate(1 day)
Compute: Lambda (Python 3.12), 60s timeout
Step 1: Start Athena query, poll for completion
Step 2: Build an ASFF finding per returned row
Step 3: BatchImportFindings -> AWS Security Hub
Severity label: HIGH
Workflow status: NEW
RelatedRequirements: NIST.800-53.r5 AC-2
Idempotency: Id = sha256(user_name + effective_date)
The deterministic finding Id keeps repeated daily runs from creating duplicates for the same unresolved account.
What a result does not prove
An enabled account does not by itself establish unauthorized access or a control failure. Identifier quality, export timing, and accounts held in other directories all remain contextual.
Validation status for this platform: Not tenant-validated. Structure, query generation, and preflight are covered by the project's automated test suites. This pack has not been executed against a live tenant, account, or index. Validate it in a non-production scope before relying on any result.
Before you act on a result
- Is the HR feed authoritative and complete for this population?
- Does an approved extension, legal hold, or rehire explain the account remaining enabled?
- Could an alias or renamed account have produced an incorrect match?
- Has downstream application access been revoked, not just the directory account?
Common questions
- How do I check for residual access after termination in AWS?
- Run the published SQL (Athena) + Lambda query on this page against Amazon Athena over IAM inventory + supplied HR feed. It reads iam_user_inventory (Config export), hr_termination_events (supplied) and reports: Identity still enabled after the recorded termination date, with no unexpired access extension.
- What permissions are needed to run this AWS query?
- athena:StartQueryExecution; glue:GetTable; s3:GetObject.
- What evidence does AC-2 need that AWS cannot produce?
- HR termination events (hr_termination_events.csv). Establishes when an individual's employment ended so directory state can be compared against it. Without this file, an enabled account is just an enabled account — there is nothing to compare it to. At minimum it must carry: user_principal_name, employment_status, effective_date.
- Does a result from this query mean the control has failed?
- An enabled account does not by itself establish unauthorized access or a control failure. Identifier quality, export timing, and accounts held in other directories all remain contextual.
Terms used on this page
- NIST SP 800-53the U.S. federal catalogue of security requirements
- The catalogue of security and privacy requirements published by the National Institute of Standards and Technology, a U.S. federal agency. U.S. government systems are measured against it, and many private organizations adopt it voluntarily. Each requirement has an identifier such as AC-2. Official source →
- CISACybersecurity and Infrastructure Security Agency
- The U.S. federal agency responsible for national critical-infrastructure cybersecurity. Its publications are works of the U.S. government and are free to use. Official source →
- CPGCross-Sector Cybersecurity Performance Goals
- A voluntary baseline of security practices published by the U.S. Cybersecurity and Infrastructure Security Agency, written specifically for organizations too small to employ security staff. It is a short, plainly written starting list rather than a full standard. Official source →
- not tenant-validatednever executed against real data
- The check has passed the project's automated tests for structure and syntax, but has never been run against real data anywhere. Test it in a safe scope before relying on any result.