Skip to content

Log Retention and Archive Verification

Overview

HIPAA §164.312(b) requires audit logs to be retained for 6 years. Cirius Group satisfies this with a two-tier architecture:

  • Hot tier (90 days): Log Analytics Workspace (cirius-logging-law-central) — query-able via KQL
  • Archive tier (6 years): Azure Blob Storage with WORM immutability — raw log export

This document covers how to verify both tiers are functioning, the archive is intact, and the evidence is audit-ready.

For AWS CloudTrail retention, see the dedicated section below.


Log Sources and Retention Targets

Log SourceHot Tier TableArchive RequiredRetention Target
Entra sign-in logsSignInLogsYes6 years
Entra audit logsAuditLogsYes6 years
Azure Activity LogAzureActivityYes6 years
Windows Security EventsSecurityEventYes6 years
Palo Alto firewall logsCommonSecurityLogYes6 years
Cortex XDR eventsCommonSecurityLogYes6 years
AWS CloudTrailS3 (separate)Yes6 years
Container App logsContainerAppConsoleLogs_CLYes6 years
HeartbeatHeartbeatNo90 days sufficient

1 — Hot Tier (LAW 90-Day Window)

Verify LAW Retention Setting

Azure Portal → Log Analytics Workspace → cirius-logging-law-central → Usage and estimated costs → Data retention

Retention should be set to 90 days. Do not pay for hot retention beyond 90 days — data older than 90 days is available via the archive export.

bash
az monitor log-analytics workspace show \
  --workspace-name cirius-logging-law-central \
  --resource-group rg-logging-logs \
  --query "retentionInDays"

Expected: 90

Verify Data Is Flowing Into LAW

bash
# Check the most recent data in key tables
az monitor log-analytics query \
  --workspace 5d76d1f2 \
  --analytics-query "SignInLogs | summarize max(TimeGenerated) by bin(TimeGenerated, 1h) | order by TimeGenerated desc | take 1" \
  --output table

If the most recent SignInLogs entry is more than 2 hours old, sign-in log ingestion may have stopped. Check:

  1. Entra ID → Diagnostic settings — is the LAW workspace still configured as a destination?
  2. LAW ingestion health: Heartbeat | summarize max(TimeGenerated) — is the workspace receiving any data?

Run the same check for other key tables:

AuditLogs | summarize max(TimeGenerated)
AzureActivity | summarize max(TimeGenerated)
SecurityEvent | summarize max(TimeGenerated)
CommonSecurityLog | summarize max(TimeGenerated)

2 — Archive Tier (LAW Data Export to Blob)

Architecture

LAW Data Export Rules continuously stream table data to an Azure Storage Account. The storage account has:

  • Immutable storage (WORM) — data cannot be modified or deleted within the retention period
  • 6-year retention policy — set at the container level
  • Blob versioning — protects against accidental overwrites

Check Data Export Rules Are Active

Azure Portal → Log Analytics Workspace → cirius-logging-law-central → Data Export

Each export rule shows its status (Enabled / Disabled) and the destination storage account. All HIPAA-required tables should have an active export rule.

bash
az monitor log-analytics data-export list \
  --workspace-name cirius-logging-law-central \
  --resource-group rg-logging-logs \
  --output table

Expected: one rule per table group (or one rule covering all tables), status = Enabled.

If an export rule is disabled:

  1. Find when it was disabled: Azure Activity Log → filter for "data export" operations
  2. Re-enable: Portal → Data Export → [rule] → Enable
  3. Note: data during the disabled period is NOT retroactively exported — this is a gap that must be documented

Verify Archive Storage Account Immutability

Azure Portal → Storage Account [archive storage account] → Data management → Immutable blob storage

Immutability policy should show:

  • Policy type: Time-based retention
  • Retention period: 2190 days (6 years)
  • State: Locked or Unlocked

Important: Leave the policy state as Unlocked unless permanently locking is explicitly required. A locked policy cannot be shortened — only extended. An unlocked policy still prevents deletion within the retention period.

bash
az storage container immutability-policy show \
  --account-name <archive-storage-account> \
  --container-name <logs-container> \
  --output json

Verify Archive Data Is Present and Recent

Spot-check that recent data has been exported:

bash
# List blobs in the archive container for today
az storage blob list \
  --account-name <archive-storage-account> \
  --container-name <logs-container> \
  --prefix "$(date +%Y/%m/%d)" \
  --output table \
  --auth-mode login

If no blobs exist for today, the export is not running. Check the export rule status and the storage account firewall settings (export requires the LAW service to reach the storage account).

Test Archive Data Readability (Annual)

During the annual DR test or compliance review, verify that archived data can be read:

  1. Download a sample blob from the archive:

    bash
    az storage blob download \
      --account-name <archive-storage-account> \
      --container-name <logs-container> \
      --name <path-to-blob> \
      --file /tmp/log-sample.json \
      --auth-mode login
  2. Verify the file is valid JSON:

    bash
    python3 -m json.tool /tmp/log-sample.json | head -50
  3. Confirm the log fields are intact (TimeGenerated, UserPrincipalName, etc.)

Record the test date and result in the audit evidence log.


3 — AWS CloudTrail Retention

CloudTrail logs are written directly to an S3 bucket in the AWS Logging account (038901680748). Separate from the LAW archive.

Verify CloudTrail Is Enabled

bash
# Check CloudTrail is running in all regions, all accounts
aws cloudtrail describe-trails \
  --profile logging \
  --include-shadow-trails true \
  --output table

The organization trail should show IsOrganizationTrail: True — this covers all 7 accounts from the management account.

Verify S3 Retention and Object Lock

bash
# Check CloudTrail bucket lifecycle (moves to Glacier after 90 days)
aws s3api get-bucket-lifecycle-configuration \
  --bucket <cloudtrail-bucket-name> \
  --profile logging

# Check Object Lock
aws s3api get-object-lock-configuration \
  --bucket <cloudtrail-bucket-name> \
  --profile logging

CloudTrail logs should have:

  • Lifecycle rule: Standard-IA at 90 days → Glacier at 365 days
  • Object Lock: COMPLIANCE mode, 2190 days (6 years)

Verify Recent CloudTrail Data

bash
# Confirm recent events are being logged
aws cloudtrail lookup-events \
  --profile logging \
  --start-time "$(date -d '1 hour ago' --iso-8601=seconds)" \
  --max-results 5

If no events in the last hour, CloudTrail delivery is broken. Check:

  1. S3 bucket policy (CloudTrail needs s3:PutObject on the bucket)
  2. CloudTrail status in the console
  3. CloudWatch Logs delivery if configured

4 — Verification Schedule

CheckFrequencyOwnerEvidence
LAW hot retention settingQuarterlyRoryPortal screenshot
LAW table ingestion (all key tables)Weekly via HIPAA audit emailAutomatedEmail report
Data Export rules — all enabledMonthlyRoryCLI output screenshot
Archive storage immutabilityQuarterlyRoryPortal/CLI screenshot
Archive blobs present for current weekMonthlyRoryCLI output
CloudTrail enabled and org-wideQuarterlyRoryCLI output
CloudTrail S3 Object LockQuarterlyRoryCLI output
Archive data readability testAnnualRoryDownloaded file + validation

What to Do When Verification Fails

FindingSeverityAction
Data Export rule disabledCriticalRe-enable immediately; document gap period; notify Rory
Archive storage immutability removedCritical — likely a security eventTreat as incident; investigate who removed it; re-enable; escalate
LAW retention reduced below 90 daysHighRestore to 90 days; investigate
CloudTrail delivery failureHighRestore delivery; the gap is an audit finding
Archive data unreadableHighEscalate; test older blobs; contact Microsoft if needed
Missing blobs for recent periodMediumCheck export rule health; determine gap size
LAW table missing recent dataMediumCheck diagnostic settings for that data source

All failures are documented. If the gap affects HIPAA-covered data, assess whether it constitutes a reportable incident under the HIPAA Breach Notification Rule.


Auditor Evidence Package

For HIPAA §164.312(b) and SOC2 A1.2/A1.3:

  1. This document — the retention policy and verification procedure
  2. LAW retention setting screenshot — 90 days confirmed
  3. Data Export rule list — shows all required tables are being exported
  4. Archive storage immutability screenshot — shows WORM policy active
  5. CloudTrail Object Lock screenshot — AWS log immutability
  6. Archive data readability test record — proves the data is usable
  7. HIPAA audit email history — weekly automated ingestion health checks


Document History

DateChangeAuthor
May 2026Initial draft — LAW hot tier (90-day), archive export rules, blob storage WORM verification, AWS CloudTrail retention, verification schedule, failure escalation, HIPAA evidence package.Rory

Internal use only — Cirius Group