This check ensures that AWS load balancers are associated with appropriate security groups, which control network traffic to and from the resource. When the security_groups attribute is omitted from an aws_lb resource, as shown below, the load balancer may become exposed to unrestricted network access, increasing the risk of unauthorized access or attacks:
resource"aws_security_group""allow_tls"{name="allow_tls"description="Allow TLS inbound traffic"vpc_id=aws_vpc.main.idingress{description="TLS from VPC"from_port=443to_port=443protocol="tcp"cidr_blocks=[aws_vpc.main.cidr_block]ipv6_cidr_blocks=[aws_vpc.main.ipv6_cidr_block]}egress{from_port=0to_port=0protocol="-1"cidr_blocks=["0.0.0.0/0"]ipv6_cidr_blocks=["::/0"]}tags={Name="allow_tls"}}resource"aws_lb""test"{name="test"load_balancer_type="application"subnets=[aws_subnet.subnet1.id,aws_subnet.subnet2.id]internal=truesecurity_groups=[aws_security_group.allow_tls.id]}
# orphan lives inside a child module and is attached there too, via an
# aws_security_group_rule in the SAME module - the module-relative
# reference (aws_security_group.orphan, never "module.child.aws_security
# _group.orphan") must be qualified with the child module's own scope
# before it can match orphan's flattened resource key
# ("module.child.orphan") in plan-JSON. See query.rego's
# reference_targets_resource-based fresh-create is_used() body.
module"child"{source="./child"}
# shared is declared inside module "parent" and used only as an input
# argument to a SECOND module nested one level inside "parent" - never
# referenced by any resource directly. The reference itself
# ("aws_security_group.shared") is relative to "parent"'s own scope, not
# the nested module's, and "parent"'s own module_calls entry for the
# nested module is only reachable via
# root_module.module_calls["parent"].module.module_calls["nested"], not
# root_module.module_calls directly.
module"parent"{source="./parent"}
Non-Compliant Code Examples
resource"aws_lb""test"{name="test"load_balancer_type="application"subnets=[aws_subnet.subnet1.id,aws_subnet.subnet2.id]internal=true}resource"aws_security_group""allow_tls"{name="allow_tls"description="Allow TLS inbound traffic"vpc_id=aws_vpc.main.idingress{description="TLS from VPC"from_port=443to_port=443protocol="tcp"cidr_blocks=[aws_vpc.main.cidr_block]ipv6_cidr_blocks=[aws_vpc.main.ipv6_cidr_block]}egress{from_port=0to_port=0protocol="-1"cidr_blocks=["0.0.0.0/0"]ipv6_cidr_blocks=["::/0"]}tags={Name="allow_tls"}}
provider"aws"{region="us-east-1"access_key="fake"secret_key="fake"skip_credentials_validation=trueskip_requesting_account_id=trueskip_metadata_api_check=true}resource"aws_vpc""main"{cidr_block="10.0.0.0/16"}resource"aws_security_group""attached"{name="attached"vpc_id=aws_vpc.main.id}# orphan is never actually attached to anything - it is only mentioned in
# this single ingress block's description text, alongside a real
# attachment (via security_groups) to a different, unrelated security
# group. Both the description and security_groups arguments are unknown
# pre-apply, so after_unknown alone cannot say which of the two
# references in the block's flat references list came from
# security_groups and which came from description.
resource"aws_security_group""orphan"{name="orphan"vpc_id=aws_vpc.main.id}resource"aws_security_group""user"{name="user"vpc_id=aws_vpc.main.idingress{description="note: orphan is ${aws_security_group.orphan.id}"from_port=443to_port=443protocol="tcp"security_groups=[aws_security_group.attached.id]}}
provider"aws"{region="us-east-1"access_key="fake"secret_key="fake"skip_credentials_validation=trueskip_requesting_account_id=trueskip_metadata_api_check=true}resource"aws_vpc""main"{cidr_block="10.0.0.0/16"}resource"aws_security_group""attached"{name="attached"vpc_id=aws_vpc.main.id}# orphan_cidr is never actually attached to anything - it is only
# referenced from an unrelated attribute (cidr_blocks) whose after_unknown
# shape is a per-element array ([true]) rather than a bare true. That
# per-element-array field must still count as "unknown" for the ambiguity
# check, alongside security_groups (a real attachment to a different
# security group): both are unresolved pre-apply, so after_unknown alone
# cannot say which of the two references in the block's flat references
# list came from security_groups and which came from cidr_blocks.
resource"aws_security_group""orphan_cidr"{name="orphan_cidr"vpc_id=aws_vpc.main.id}resource"aws_security_group""user"{name="user"vpc_id=aws_vpc.main.idingress{cidr_blocks=[aws_security_group.orphan_cidr.id]from_port=443to_port=443protocol="tcp"security_groups=[aws_security_group.attached.id]}}
1
2
rulesets:- Terraform / AWS # Rules to enforce / AWS.
Request a personalized demo
Get Started with Datadog
Ask AI
AI-generated responses may be inaccurate. Verify important info.