Skip to main content

HIPAA Per-Control CLI Evidence

Control-by-control mapping to AWS docs, the AWS CLI command to collect evidence, the CLI output shape to expect, and the pass/fail JSON structures used in the observation schema.

AWS docs as source of truth. Where AWS docs show an explicit CLI example, that shape is used directly. Where AWS docs define the response schema but do not show the exact S3 example, the JSON is marked as representative of the documented output schema.

See also: HIPAA S3 control mapping for the full HIPAA-section-to-control table, and the HIPAA Compliance how-to for running the HIPAA pack.


AUDIT.002 — object-level logging

What AWS says

S3 object-level logging is a CloudTrail data event concern, not S3 server access logging. AWS states that data events include S3 object API activity such as GetObject, PutObject, and DeleteObject, and that trails do not log data events by default. AWS also documents that AWS::S3::Object is the resource type used in event selectors.

CLI evidence

aws cloudtrail get-event-selectors --trail-name <trail-name>

CLI output shown by AWS docs

{
"EventSelectors": [
{
"IncludeManagementEvents": true,
"DataResources": [],
"ReadWriteType": "All"
}
],
"TrailARN": "arn:aws:cloudtrail:us-east-1:123456789012:trail/Trail1"
}

A trail without matching data event selectors will not log S3 object data events.

Pass JSON structure

Use this when at least one selector covers AWS::S3::Object for the target bucket, prefix, or all S3 buckets.

{
"audit": {
"object_level_logging": {
"enabled": true,
"source": "cloudtrail",
"trail_arn": "arn:aws:cloudtrail:us-east-1:123456789012:trail/org-trail",
"selectors": [
{
"read_write_type": "All",
"include_management_events": true,
"data_resources": [
{
"type": "AWS::S3::Object",
"values": [
"arn:aws:s3:::example-bucket/"
]
}
]
}
]
}
}
}

Fail JSON structure

Use this when no selector covers S3 object data events.

{
"audit": {
"object_level_logging": {
"enabled": false,
"source": "cloudtrail",
"trail_arn": "arn:aws:cloudtrail:us-east-1:123456789012:trail/Trail1",
"selectors": [
{
"read_write_type": "All",
"include_management_events": true,
"data_resources": []
}
],
"reason": "No CloudTrail data event selector for AWS::S3::Object"
}
}
}

ACCESS.003 — VPC endpoint

What AWS says

AWS documents that Amazon S3 supports gateway and interface VPC endpoints. A gateway endpoint lets a VPC access S3 without an internet gateway or NAT. AWS CLI describe-vpc-endpoints returns endpoint objects with fields such as VpcEndpointType, ServiceName, VpcEndpointId, RouteTableIds, and PolicyDocument.

CLI evidence

aws ec2 describe-vpc-endpoints \
--filters Name=service-name,Values=com.amazonaws.<region>.s3

Representative CLI output shape from AWS docs

{
"VpcEndpoints": [
{
"PolicyDocument": "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"*\",\"Resource\":\"*\"}]}",
"VpcId": "vpc-12345678",
"RouteTableIds": [
"rtb-11111111"
],
"State": "available",
"ServiceName": "com.amazonaws.us-east-1.s3",
"VpcEndpointId": "vpce-0123456789abcdef0",
"VpcEndpointType": "Gateway",
"CreationTimestamp": "2026-03-30T10:00:00Z",
"OwnerId": "123456789012"
}
]
}

Pass JSON structure

{
"network": {
"s3_vpc_endpoints": [
{
"vpc_endpoint_id": "vpce-0123456789abcdef0",
"service_name": "com.amazonaws.us-east-1.s3",
"vpc_endpoint_type": "Gateway",
"state": "available",
"vpc_id": "vpc-12345678",
"route_table_ids": [
"rtb-11111111"
],
"present": true
}
]
}
}

Fail JSON structure

{
"network": {
"s3_vpc_endpoints": [],
"s3_vpc_endpoint_required": true,
"reason": "No S3 VPC endpoint found for service com.amazonaws.us-east-1.s3"
}
}

ACCESS.006 — VPC endpoint policy

What AWS says

AWS says a VPC endpoint policy is a JSON resource policy attached to the endpoint. If you do not attach one, AWS attaches the default endpoint policy, which grants full access. For gateway endpoints, Principal must be *. AWS CLI returns the endpoint PolicyDocument in describe-vpc-endpoints.

CLI evidence

aws ec2 describe-vpc-endpoints \
--filters Name=service-name,Values=com.amazonaws.<region>.s3

Key AWS default policy

{
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "*",
"Resource": "*"
}
]
}

AWS documents this as the default endpoint policy.

Pass JSON structure

Use this when the endpoint policy is not the default full-access policy and matches your restriction model.

{
"network": {
"s3_vpc_endpoint_policy": {
"attached": true,
"is_default_full_access": false,
"vpc_endpoint_id": "vpce-0123456789abcdef0",
"policy": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::example-bucket/*"
],
"Condition": {
"StringEquals": {
"aws:PrincipalArn": "arn:aws:iam::123456789012:role/app-role"
}
}
}
]
}
}
}
}

Fail JSON structure

Use this when the endpoint policy is missing or is the default full-access policy.

{
"network": {
"s3_vpc_endpoint_policy": {
"attached": true,
"is_default_full_access": true,
"vpc_endpoint_id": "vpce-0123456789abcdef0",
"policy": {
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "*",
"Resource": "*"
}
]
},
"reason": "Endpoint policy is default full-access policy"
}
}
}

ACCESS.009 — presigned URL restriction

What AWS says

AWS documents S3 Signature Version 4 condition keys for bucket policies, including s3:signatureAge and s3:authType. AWS gives explicit bucket policy examples: one denies presigned URL requests when the signature is older than 10 minutes, and one denies requests unless authentication uses the Authorization header, which blocks POST or presigned URL requests. AWS CLI get-bucket-policy returns the bucket policy as a JSON string.

CLI evidence

aws s3api get-bucket-policy --bucket <bucket-name>

CLI output shown by AWS docs

{
"Policy": "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"s3:GetObject\",\"Resource\":\"arn:aws:s3:::amzn-s3-demo-bucket/*\"},{\"Sid\":\"\",\"Effect\":\"Deny\",\"Principal\":\"*\",\"Action\":\"s3:GetObject\",\"Resource\":\"arn:aws:s3:::amzn-s3-demo-bucket/secret/*\"}]}"
}

The Policy field is a JSON document returned as a string.

Pass JSON structure

Use this when the bucket policy contains a deny guardrail with S3 SigV4 condition keys relevant to presigned URLs.

{
"access": {
"presigned_url_restriction": {
"bucket_policy_present": true,
"restricted": true,
"policy_checks": {
"has_signature_age_guardrail": true,
"signature_age_ms_max": 600000,
"has_auth_type_guardrail": true,
"allowed_auth_type": "REST-HEADER"
},
"matching_statements": [
{
"sid": "DenyOldPresignedUrls",
"effect": "Deny",
"condition": {
"NumericGreaterThan": {
"s3:signatureAge": 600000
}
}
},
{
"sid": "DenyPresignedUrls",
"effect": "Deny",
"condition": {
"StringNotEquals": {
"s3:authType": "REST-HEADER"
}
}
}
]
}
}
}

Fail JSON structure

Use this when the bucket policy is absent or present but lacks both condition keys.

{
"access": {
"presigned_url_restriction": {
"bucket_policy_present": true,
"restricted": false,
"policy_checks": {
"has_signature_age_guardrail": false,
"has_auth_type_guardrail": false
},
"reason": "Bucket policy does not contain s3:signatureAge or s3:authType guardrails"
}
}
}

Observation areas for these controls

Based on AWS's configuration boundaries, these controls draw on the following observation areas:

{
"audit": {
"object_level_logging": {
"enabled": true,
"source": "cloudtrail",
"trail_arn": "string",
"selectors": []
}
},
"network": {
"s3_vpc_endpoints": [],
"s3_vpc_endpoint_policy": {
"attached": true,
"is_default_full_access": false,
"policy": {}
}
},
"access": {
"presigned_url_restriction": {
"bucket_policy_present": true,
"restricted": true,
"policy_checks": {
"has_signature_age_guardrail": true,
"has_auth_type_guardrail": true
}
}
}
}

This matches AWS's actual service split:

  • CloudTrail for S3 object data events.
  • EC2/VPC for S3 endpoint inventory and endpoint policy.
  • S3 bucket policy for presigned URL restrictions.

Directly testable HIPAA controls — CLI evidence detail

For each directly testable item: the HIPAA section, the AWS source, the AWS CLI command, the CLI output shape, the pass JSON, and the fail JSON. HIPAA defines the requirement; the AWS CLI provides the evidence.

Encryption

HIPAA mapping

45 CFR § 164.312(a)(2)(iv) – Encryption and decryption. The rule says to implement a mechanism to encrypt and decrypt electronic protected health information. It is an addressable implementation specification, not an automatic exemption.

AWS source

AWS documents S3 bucket default encryption and the get-bucket-encryption CLI. AWS also states that buckets now have default encryption using SSE-S3 unless configured otherwise, and the API returns the active encryption configuration.

CLI command

aws s3api get-bucket-encryption --bucket <bucket-name>

CLI output shape

{
"ServerSideEncryptionConfiguration": {
"Rules": [
{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:us-east-1:123456789012:key/11111111-2222-3333-4444-555555555555"
},
"BucketKeyEnabled": true
}
]
}
}

Pass JSON

{
"encryption": {
"enabled": true,
"mode": "aws:kms",
"kms_key_id": "arn:aws:kms:us-east-1:123456789012:key/11111111-2222-3333-4444-555555555555",
"bucket_key_enabled": true
}
}

Fail JSON

{
"encryption": {
"enabled": false,
"mode": null,
"kms_key_id": null,
"bucket_key_enabled": false,
"reason": "No compliant default bucket encryption configuration found"
}
}

Public access block

HIPAA mapping

This supports 45 CFR § 164.312(a)(1) – Access Control, which requires technical policies and procedures to allow access only to authorized persons or software programs. Public access block is not named in HIPAA, but it is a strong AWS control for preventing unauthorized public exposure.

AWS source

AWS documents bucket-level public access block and the four flags returned by get-public-access-block. AWS also notes that effective behavior can also depend on account-level settings.

CLI command

aws s3api get-public-access-block --bucket <bucket-name>

CLI output shape

{
"PublicAccessBlockConfiguration": {
"BlockPublicAcls": true,
"IgnorePublicAcls": true,
"BlockPublicPolicy": true,
"RestrictPublicBuckets": true
}
}

Pass JSON

{
"public_access": {
"block_public_acls": true,
"ignore_public_acls": true,
"block_public_policy": true,
"restrict_public_buckets": true,
"compliant": true
}
}

Fail JSON

{
"public_access": {
"block_public_acls": true,
"ignore_public_acls": false,
"block_public_policy": true,
"restrict_public_buckets": false,
"compliant": false,
"reason": "One or more PublicAccessBlock settings are not enabled"
}
}

Transport security

HIPAA mapping

45 CFR § 164.312(e)(1) – Transmission security requires technical security measures to guard against unauthorized access to ePHI transmitted over an electronic communications network.

AWS source

AWS recommends using a bucket policy with aws:SecureTransport to deny non-HTTPS requests. AWS gives explicit example policies. get-bucket-policy returns the bucket policy as a JSON string.

CLI command

aws s3api get-bucket-policy --bucket <bucket-name>

CLI output shape

The CLI returns this wrapper shape, with the policy itself inside the Policy string.

{
"Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Deny\",\"Principal\":\"*\",\"Action\":\"s3:*\",\"Resource\":[\"arn:aws:s3:::example-bucket\",\"arn:aws:s3:::example-bucket/*\"],\"Condition\":{\"Bool\":{\"aws:SecureTransport\":\"false\"}}}]}"
}

Pass JSON

{
"transport_security": {
"bucket_policy_present": true,
"https_only_enforced": true,
"matching_statement": {
"effect": "Deny",
"condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
}
}

Fail JSON

{
"transport_security": {
"bucket_policy_present": true,
"https_only_enforced": false,
"reason": "Bucket policy does not deny requests where aws:SecureTransport is false"
}
}

Logging

HIPAA mapping

45 CFR § 164.312(b) – Audit controls requires mechanisms that record and examine activity in information systems that contain or use ePHI. AWS S3 evidence splits into two parts:

  • server access logging
  • CloudTrail data events for object-level access

AWS source

AWS documents get-bucket-logging for server access logging and CloudTrail data events for object operations. AWS also states that S3 object data events are CloudTrail data events and are not logged by default unless configured on the trail.

CLI commands

aws s3api get-bucket-logging --bucket <bucket-name>
aws cloudtrail get-event-selectors --trail-name <trail-name>

CLI output shapes

Server access logging

{
"LoggingEnabled": {
"TargetPrefix": "",
"TargetBucket": "example-bucket-logs"
}
}

CloudTrail data events

{
"EventSelectors": [
{
"IncludeManagementEvents": true,
"DataResources": [
{
"Type": "AWS::S3::Object",
"Values": [
"arn:aws:s3:::example-bucket/"
]
}
],
"ReadWriteType": "All"
}
],
"TrailARN": "arn:aws:cloudtrail:us-east-1:123456789012:trail/org-trail"
}

Pass JSON

{
"logging": {
"server_access_logging": {
"enabled": true,
"target_bucket": "example-bucket-logs",
"target_prefix": ""
},
"object_level_logging": {
"enabled": true,
"source": "cloudtrail",
"trail_arn": "arn:aws:cloudtrail:us-east-1:123456789012:trail/org-trail",
"selectors": [
{
"read_write_type": "All",
"data_resources": [
{
"type": "AWS::S3::Object",
"values": [
"arn:aws:s3:::example-bucket/"
]
}
]
}
]
},
"compliant": true
}
}

Fail JSON

{
"logging": {
"server_access_logging": {
"enabled": false
},
"object_level_logging": {
"enabled": false
},
"compliant": false,
"reason": "Neither bucket server access logging nor CloudTrail S3 object data event logging is configured"
}
}

Integrity

HIPAA mapping

45 CFR § 164.312(c)(1) – Integrity requires policies and procedures to protect ePHI from improper alteration or destruction. S3 Versioning and Object Lock are strong AWS controls for this.

AWS source

AWS documents get-bucket-versioning and states it returns the bucket versioning state and MFA delete state. AWS also documents GetObjectLockConfiguration for object lock configuration.

CLI commands

aws s3api get-bucket-versioning --bucket <bucket-name>
aws s3api get-object-lock-configuration --bucket <bucket-name>

CLI output shapes

Versioning

{
"Status": "Enabled",
"MFADelete": "Enabled"
}

Object Lock

{
"ObjectLockConfiguration": {
"ObjectLockEnabled": "Enabled",
"Rule": {
"DefaultRetention": {
"Mode": "COMPLIANCE",
"Days": 30
}
}
}
}

Pass JSON

{
"integrity": {
"versioning": {
"enabled": true,
"mfa_delete": true
},
"object_lock": {
"enabled": true,
"mode": "COMPLIANCE",
"days": 30
},
"compliant": true
}
}

Fail JSON

{
"integrity": {
"versioning": {
"enabled": false,
"mfa_delete": false
},
"object_lock": {
"enabled": false
},
"compliant": false,
"reason": "Bucket versioning is not enabled and object lock is not configured"
}
}

Mappable, but not fully checkable from a bucket-only S3 snapshot

These are real HIPAA requirements, but they need more than bucket config.

§ 164.312(a)(1) – Access Control

HIPAA requires technical policies and procedures for access control. In AWS S3, that can involve IAM roles, bucket policies, ACLs, VPC endpoint policies, KMS key policies, and application identity boundaries. Bucket-only evidence is not enough to prove full compliance.

What you can check

  • public access block
  • bucket policy scope
  • absence of public ACL/policy
  • optional MFA delete

What you cannot prove from bucket-only data

  • least privilege across IAM roles
  • app-to-prefix scoping across all callers
  • KMS authorization path
  • network path constraints unless VPC endpoint data is included

Partial pass JSON

{
"access_control": {
"bucket_level_controls_present": true,
"public_exposure_blocked": true,
"least_privilege_globally_verified": false,
"reason": "Bucket-only evidence cannot prove IAM-wide least privilege"
}
}

§ 164.308(a)(1)(ii)(D) – Information system activity review

HIPAA requires regular review of logs, access reports, and security incident tracking reports. That is a process requirement, not just a configuration requirement. Logs existing is necessary, but not sufficient.

What AWS config can prove

  • logs are being generated

What it cannot prove

  • someone reviews them regularly
  • review procedures exist
  • alerts are triaged
  • evidence is retained for audit

Partial pass JSON

{
"activity_review": {
"logs_collected": true,
"regular_review_process_verified": false,
"reason": "Configuration shows logging, not operational review"
}
}

§ 164.308(a)(5)(ii)(B) – Protection from malicious software

HIPAA requires procedures for guarding against, detecting, and reporting malicious software. S3 by itself does not scan uploaded files. This usually needs GuardDuty Malware Protection, Lambda-based scanning, AV pipeline, or another scanning system.

Bucket-only limitation

You cannot prove malware protection from standard S3 bucket config alone.

Partial pass JSON

{
"malware_protection": {
"bucket_only_evidence": false,
"requires_additional_service_data": true,
"reason": "Need GuardDuty, Lambda scanning, or equivalent evidence"
}
}

§ 164.502(b) – Minimum necessary

HIPAA says reasonable efforts must limit PHI to the minimum necessary. In AWS this maps well to IAM policy scoping and prefix-level access boundaries, but this is not provable from bucket config alone.

What you need

  • IAM role policies
  • bucket policy conditions
  • app identity mapping
  • prefix/resource scope rules

Partial pass JSON

{
"minimum_necessary": {
"bucket_policy_scope_checked": true,
"iam_scope_verified": false,
"reason": "Need IAM policy evidence to verify minimum necessary access"
}
}

Breach notification rule – §§ 164.400–414

The breach notification rule governs what happens after discovery of a breach of unsecured PHI. Detection tooling such as GuardDuty or AWS Config may help, but those tools are not the rule itself. The rule focuses on breach determination and notification duties.

What AWS config can support

  • fast detection of public exposure
  • evidence for whether access occurred
  • logs for incident investigation

What it cannot prove

  • legal breach determination
  • notice timelines
  • required notifications to individuals, HHS, or media

Partial pass JSON

{
"breach_notification_support": {
"misconfiguration_detection_present": true,
"incident_evidence_available": true,
"legal_notification_compliance_verified": false,
"reason": "Need incident response and legal workflow evidence"
}
}