ECS services should not have public IP addresses assigned automatically
ECS tasks with public IP addresses are directly reachable from the internet, bypassing any load balancer or NAT gateway that would otherwise mediate and filter traffic. Each task launch creates a new internet-facing endpoint that can expose application vulnerabilities, debug ports, or sidecar containers.
Private tasks behind an ALB or NAT gateway give you a single, auditable ingress point. TLS termination, WAF rules, and security group chaining are managed in one place rather than replicated across every ephemeral task ENI.
Retrofit consideration
Existing services with public IPs may depend on direct internet connectivity for outbound calls to AWS APIs or external endpoints. Removing public IPs means adding a NAT gateway or VPC endpoints, both of which change network topology and increase cost.
Implementation
Choose the approach that matches how you manage Terraform.
If you use terraform-aws-modules/ecs/aws//modules/service, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.
module "ecs" {
source = "terraform-aws-modules/ecs/aws//modules/service"
version = ">=6.0.0"
cluster_arn = "arn:aws:ecs:us-east-1:123456789012:cluster/example-cluster"
container_definitions = {
main = {
essential = true
image = "public.ecr.aws/docker/library/httpd:latest"
portMappings = [
{
containerPort = 80
protocol = "tcp"
}
]
}
}
cpu = 256
memory = 512
name = "abc123"
subnet_ids = ["subnet-12345678"]
assign_public_ip = false
}
Use AWS provider resources directly. See docs for the resources involved: aws_ecs_service.
resource "aws_ecs_service" "this" {
cluster = "arn:aws:ecs:us-east-1:123456789012:cluster/example-cluster"
launch_type = "FARGATE"
name = "pofix-abc123"
task_definition = "arn:aws:ecs:us-east-1:123456789012:task-definition/example-task:1"
network_configuration {
assign_public_ip = false
subnets = ["subnet-abc123", "subnet-def456"]
}
}
What this control checks
The aws_ecs_service resource includes a network_configuration block when the associated task definition uses awsvpc network mode. That block accepts an assign_public_ip argument, which defaults to false when omitted. The control fails when assign_public_ip is explicitly set to true. Services not using awsvpc network mode have no network_configuration block and are out of scope for this check. Since awsvpc is mandatory for Fargate, this control applies to all Fargate workloads.
Common pitfalls
Fargate tasks lose outbound internet without NAT or VPC endpoints
Set
assign_public_ip = falseand Fargate tasks lose direct outbound internet connectivity immediately. They can still reach private destinations if routing and security groups allow it, but public AWS APIs and external endpoints go dark. Route egress through a NAT gateway, or provision VPC endpoints for ECR, S3, CloudWatch Logs, and any other AWS services the tasks call.Default value is safe but implicit
Omitting
assign_public_ipfromnetwork_configurationis safe, the argument defaults tofalse. But an engineer reading the Terraform later won't know whether the omission was intentional or an oversight. Explicitly settingassign_public_ip = falsesignals that the constraint is deliberate and guards against accidental changes during refactoring.Service discovery may mask public exposure
ECS services registered with Cloud Map can still have public IPs on task ENIs even when consumers reach them through private DNS. Cloud Map doesn't prevent direct inbound internet traffic if security groups permit it. Audit both the
assign_public_ipsetting and the inbound rules on task ENI security groups.CodeDeploy blue/green deployments recreate network configuration
With
deployment_controllertypeCODE_DEPLOY, the replacement task set picks up its network configuration from the AppSpec or deployment group, not from theaws_ecs_serviceresource. If the CodeDeploy configuration doesn't specifyassignPublicIp: DISABLED, you can end up with public IPs on blue/green replacement tasks even when the Terraform resource is clean.
Audit evidence
Config rule evaluation results showing all ECS services with assignPublicIp: DISABLED are the primary evidence artifact. A Security Hub findings export filtered to this control, showing zero non-compliant resources, works as point-in-time evidence for most auditors. Console screenshots of ECS service networking configuration confirming private subnets and no public IP assignment provide supporting documentation. VPC Flow Logs showing task ENIs communicating only through internal load balancer or NAT gateway IPs, and never to internet-routable addresses directly, can back up the network isolation claim if the auditor asks for it.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
ecs_service_not_publicly_accessibleCheckov Check:
CKV_AWS_333Powerpipe Control:
aws_compliance.control.ecs_service_not_publicly_accessibleProwler Checks:
ecs_service_no_assign_public_ip,ecs_task_set_no_assign_public_ipAWS Security Hub Controls:
ECS.16,ECS.2KICS Query:
bafe7989-3c4b-47f0-910b-e6e1cba7f146
Last reviewed: 2026-03-09