CFN detector parity audit¶
This page is the load-bearing matrix that lets us be precise about what "CloudFormation supported" means in Efterlev. Per the v0.1.95 graduation plan: each of the 60 KSI-mapped detectors is classified by whether it fires on CFN-translated bodies equivalently to Terraform.
The matrix is generated by
scripts/build_cfn_parity_table.py,
which inspects:
- The registered CFN→TF mappings in
src/efterlev/cloudformation/property_mapping.py's_MAPPINGSdict. - Each mapping function's emitted
tf_type(handles 1→1+N synthesis for IAM/S3/Events). - Each detector's
r.type ==filter (which TF resource types it reads). - Manual-review tables for:
- TF type aliases (
aws_alb↔aws_lb,aws_alb_listener↔aws_lb_listener). - TF data sources with no CFN concept (
aws_iam_policy_document). - TF-only resources with no CFN counterpart in AWS at all
(
aws_iam_account_password_policy— account-level setting, deployable only via the IAM API). - TF sub-resources CFN bundles inline (
aws_s3_bucket_versioningetc., where CFN'sAWS::S3::Bucketcarries the same property data in a nested block).
Coverage summary at v0.1.96¶
Authoritative data: docs/cfn-detector-parity.csv.
| Status | Count | Meaning |
|---|---|---|
| full | 47 | Every TF type the detector reads has a mapped CFN equivalent. Detector fires on CFN identically to TF. |
| partial-by-design | 11 | Some TF types are mapped; the unmapped ones are either CFN-bundled-into-parent (S3 sub-resources, etc.) or TF-only-by-design (data sources). No real coverage gap. |
| TF-only-by-design | 2 | Detector reads only TF-side concepts (data sources, account-level settings, IAM-API-only resources). No CFN equivalent exists. |
Effective CFN coverage: 100% (full + partial-by-design + TF-only-by-design = 60 of 60). Zero real gaps.
v0.1.96 closed the v0.1.95 gaps¶
The v0.1.95 audit surfaced 5 detectors with real CFN coverage gaps
(8 unmapped CFN types total). v0.1.96 (PR gamma.2 batch 9) closed all
of them by mapping AWS::IAM::AccessKey, AWS::Logs::Destination,
AWS::Logs::SubscriptionFilter, AWS::OpenSearchService::Domain,
AWS::Elasticsearch::Domain, AWS::KinesisFirehose::DeliveryStream,
AWS::SecurityHub::Hub, and AWS::SecurityHub::FindingAggregator.
TF-only by design (intentional, no fix planned)¶
| Detector | Reason |
|---|---|
aws.iam_password_policy |
The IAM account password policy is an account-level setting in AWS, deployable only via the IAM API or AWS Console. CFN has no resource type for it. TF's aws_iam_account_password_policy abstracts the API call. CFN users hit this control via the IAM API directly — outside Efterlev's IaC scope. |
aws.iam_managed_via_terraform |
Meta-detector: confirms the workspace declares any aws_iam_* resources at all. By definition Terraform-only — exists to call out boundary scope when a workspace has zero IAM in TF. |
aws.iam_service_account_keys_age (partial-by-design) |
Reads aws_iam_access_key (now CFN-mapped at v0.1.96) AND aws_iam_user_login_profile (no CFN equivalent — AWS exposes login-profile creation only via the IAM API). The detector still fires on CFN-translated AccessKey; the user-login-profile read path is a TF-only data point. |
Sub-resource bundling: how the CFN side handles TF separation-of-concerns¶
Terraform splits some AWS resources into multiple separate TF resource
types where CloudFormation bundles them inline. For example,
aws_s3_bucket + aws_s3_bucket_versioning +
aws_s3_bucket_server_side_encryption_configuration + ... are all
modeled as nested blocks under CFN's AWS::S3::Bucket.
The property mapping table handles this in two ways:
- 1→1+N synthesis when the detector reads the sub-resource type directly. v0.1.73's
_map_s3_bucketsynthesizesaws_s3_bucket_public_access_blockfromPublicAccessBlockConfiguration; gamma.2 batch 2 (v0.1.75) does the same foraws_iam_role_policy_attachmentperManagedPolicyArnsentry, etc. - Parent-body access when the detector reads the property from the
parent type's body.
aws.encryption_s3_at_restreadsaws_s3_bucket.server_side_encryption_configuration(or the separateaws_s3_bucket_server_side_encryption_configurationresource); the CFN side surfaces both via the parent bucket's body.
Sub-resources that v0.1.95 doesn't yet auto-synthesize but bundles into the parent CFN type:
| TF sub-resource | Parent CFN type | Status |
|---|---|---|
aws_s3_bucket_versioning |
AWS::S3::Bucket |
Bundled in parent body via VersioningConfiguration; not synthesized. Detectors that reach into the parent body see the data; standalone-resource lookups don't. |
aws_s3_bucket_acl |
AWS::S3::Bucket |
Bundled via AccessControl; not synthesized. |
aws_s3_bucket_server_side_encryption_configuration |
AWS::S3::Bucket |
Bundled via BucketEncryption; not synthesized. |
aws_s3_bucket_lifecycle_configuration |
AWS::S3::Bucket |
Bundled via LifecycleConfiguration; not synthesized. |
aws_s3_bucket_public_access_block |
AWS::S3::Bucket |
ALREADY SYNTHESIZED via 1→1+N from batch 1 (v0.1.74). Detectors that filter on this type fire on CFN. |
aws_s3_bucket_replication_configuration |
AWS::S3::Bucket |
Bundled via ReplicationConfiguration; not synthesized. |
aws_rds_cluster_instance |
AWS::RDS::DBCluster |
CFN clusters bundle member instances via separate AWS::RDS::DBInstance with DBClusterIdentifier; not auto-synthesized as aws_rds_cluster_instance. |
Regenerating the matrix¶
Outputs docs/cfn-detector-parity.csv (authoritative) and reports a
status breakdown. Re-run whenever a new detector lands or the property
mapping table changes; the matrix should stay in sync.