| Tested on | Ubuntu 26.04 LTS (Resolute Raccoon) |
|---|---|
| Package | terraform 1.16.1hashicorp/aws 6.63.0awscli 2.36.38 |
| Applies to | Any host with Terraform and the AWS provider; examples use ap-south-1 |
| Privilege | AWS identity that can describe and delete VPC resources; normal user on the workstation |
| Scope | Fix DependencyViolation: The vpc 'vpc-…' has dependencies and cannot be deleted during terraform destroy — find real AWS blockers with the CLI, recover when child resources were removed from state with terraform state rm, re-import with terraform import, and destroy in dependency order. Does not cover EKS or managed-service ENI delays, multi-account networking, or replacing a VPC in place. |
terraform destroy on an AWS VPC stack can fail near the end with a message that sounds vague:
Error: deleting EC2 VPC (vpc-03cbdc25f0ea9da5f): operation error EC2: DeleteVpc, …
api error DependencyViolation: The vpc 'vpc-03cbdc25f0ea9da5f' has dependencies and cannot be deleted.In plain terms, AWS will not delete the VPC until everything inside it is gone first.
What usually causes DependencyViolation
- A subnet, instance, or network interface still exists in AWS
- An internet gateway is still attached
- Something was created in the console and never added to Terraform state
- Child resources were removed from Terraform state with
terraform state rm, but the real AWS objects were left running
Terraform only destroys what it tracks in state. When state and AWS do not match, destroy may try the VPC while children still exist in your account.
This guide shows how to read the error, list what AWS still holds inside the VPC, and recover with terraform import or manual teardown.
terraform plan fails with No valid credential sources found, fix auth first — see Fix Terraform AWS credentials error.
What the VPC DependencyViolation error means
DependencyViolation is an EC2 API response, not a Terraform bug. AWS runs DeleteVpc only after every dependent object is gone.
Common blockers inside the VPC:
- EC2 instances and their primary ENIs
- Subnets (while instances or ENIs are attached)
- Internet gateway attachments
- Non-default security groups that still exist in the VPC
- Custom route tables (orphaned after manual subnet deletion)
- Resources created in the console or by another tool that Terraform never tracked
An attached ENI can also prevent a security group itself from being deleted, even when no instance is running.
When destroy works normally: every child resource is still in state. terraform destroy deletes in dependency order — instance first, then subnet, gateway, security group, VPC.
When you see this error: Terraform tries to delete the VPC while AWS still has children that are not in the destroy plan.
When only the VPC remains in state
A frequent trigger: someone ran terraform state rm on child resources during a rushed cleanup. The real EC2 instance, subnet, and gateway stay in AWS, but state lists only aws_vpc.main (or your VPC resource address).
What that looks like in state:
If your state already matches the broken pattern — VPC only, children missing — skip to Find what still blocks the VPC. To confirm the failure mode, list what Terraform tracks from your project directory:
terraform state listSample output:
data.aws_ami.ubuntu
data.aws_availability_zones.available
aws_instance.target
aws_internet_gateway.pentest
aws_route_table.public
aws_route_table_association.public
aws_security_group.target
aws_subnet.public
aws_vpc.pentestRemove every managed child of the VPC from state — leave only the VPC resource and data sources. Example after state rm on a stack with one instance, subnet, gateway, route table, association, and security group:
terraform state rm aws_instance.target aws_subnet.public aws_internet_gateway.pentest aws_route_table_association.public aws_route_table.public aws_security_group.targetTerraform confirms six removals. Verify only the VPC remains in state:
terraform state listSample output:
data.aws_ami.ubuntu
data.aws_availability_zones.available
aws_vpc.pentestRun destroy. Terraform now plans to delete only the VPC, while the EC2 instance, subnet, and gateway still exist in AWS:
terraform destroy -auto-approveThe provider may retry VPC deletion for some time while AWS still reports dependencies, printing Still destroying... lines, then fails:
aws_vpc.pentest: Destroying... [id=vpc-03cbdc25f0ea9da5f]
aws_vpc.pentest: Still destroying... [id=vpc-03cbdc25f0ea9da5f, 01m30s elapsed]
╷
│ Error: deleting EC2 VPC (vpc-03cbdc25f0ea9da5f): operation error EC2: DeleteVpc, https response error StatusCode: 400, RequestID: 76edd633-02b0-48f6-9e32-bc499245102c, api error DependencyViolation: The vpc 'vpc-03cbdc25f0ea9da5f' has dependencies and cannot be deleted.
╵The VPC row may still appear in state after the error. That is expected — AWS rejected the delete, so Terraform keeps tracking it.
Find what still blocks the VPC
Set shell variables from your stack so the inspection commands stay short. Use terraform output when outputs exist, or copy the VPC ID from the error line:
VPC_ID=$(terraform output -raw vpc_id)
REGION=ap-south-1List network interfaces in the VPC. An ENI means something still has a network attachment inside the VPC.
Check these columns in the output:
Instance— EC2 instance ID when the ENI belongs to a VMType— interface type (interface,efa, etc.)RequesterManaged—Trueoften means Lambda, ELB, ECS, or another AWS service owns the ENIDesc— service name or purpose string from AWS
aws ec2 describe-network-interfaces --filters Name=vpc-id,Values="$VPC_ID" --region "$REGION" --query 'NetworkInterfaces[].{Id:NetworkInterfaceId,Status:Status,Instance:Attachment.InstanceId,Type:InterfaceType,RequesterManaged:RequesterManaged,Desc:Description}' --output tableSample output:
---------------------------------------------------------------------------------------------------------
| DescribeNetworkInterfaces |
+------+---------------------+---------+-------------------+--------------------------------+-----------+
| Desc | Id | Instance| RequesterManaged | Status | Type |
+------+---------------------+---------+-------------------+--------------------------------+-----------+
| | eni-0a1b2c3d4e5f67890 | i-04d68e0520c9d0aa5 | False | in-use | interface |
+------+---------------------+---------+-------------------+--------------------------------+-----------+The instance ID in the table is the blocker Terraform no longer manages. Match it to resources you removed from state or created outside Terraform.
RequesterManaged: False+ an instance ID → likely EC2RequesterManaged: Trueor a service name inDesc→ likely Lambda, ELB, ECS, or another managed service
Check subnets and the internet gateway next:
aws ec2 describe-subnets --filters Name=vpc-id,Values="$VPC_ID" --region "$REGION" --query 'Subnets[].SubnetId' --output textSample output:
subnet-0b3be385af2728630List internet gateways still attached to the VPC:
aws ec2 describe-internet-gateways --filters Name=attachment.vpc-id,Values="$VPC_ID" --region "$REGION" --query 'InternetGateways[].InternetGatewayId' --output textSample output:
igw-0bf5307431a34eedcIf you use the AWS CLI regularly, run the same style of filter for security groups, route tables, and NAT gateways. Delete children before the VPC on every path.
Fix by re-importing children, then destroy
Use this path when the real AWS objects still match your .tf files.
Steps:
terraform importeach child resource back into state- Confirm
terraform state listshows the full stack again - Run
terraform destroyand read the plan before typingyes
Terraform can then delete children before the VPC, in the correct order.
Import the subnet:
terraform import aws_subnet.public subnet-0b3be385af2728630Import exits with Import successful! when the address resolves. Import the internet gateway:
terraform import aws_internet_gateway.pentest igw-0bf5307431a34eedcImport the security group:
terraform import aws_security_group.target sg-0a2a6e26ae449e908Import the public route table:
terraform import aws_route_table.public rtb-0bbbaa5c3e76438d4Route table associations use subnet-id/route-table-id as the import ID:
terraform import aws_route_table_association.public subnet-0b3be385af2728630/rtb-0bbbaa5c3e76438d4Import the target instance:
terraform import aws_instance.target i-04d68e0520c9d0aa5Confirm state again lists every managed resource:
terraform state listSample output:
aws_instance.target
aws_internet_gateway.pentest
aws_route_table.public
aws_route_table_association.public
aws_security_group.target
aws_subnet.public
aws_vpc.pentestRun a full destroy and read the plan before you confirm — Terraform should delete the instance and subnet before the VPC:
terraform destroyType yes when the destroy plan lists every child resource. Sample output:
aws_instance.target: Destruction complete after 26s
aws_subnet.public: Destruction complete after 1s
aws_security_group.target: Destruction complete after 2s
aws_vpc.pentest: Destruction complete after 2s
Destroy complete! Resources: 7 destroyed.That is the preferred fix when configuration still describes the real objects. Replace the IDs below with yours from describe-subnets, describe-internet-gateways, and describe-instances. For import syntax beyond this walkthrough, see Terraform import and Terraform state command.
Fix by deleting AWS dependencies manually
Use this path when import is not practical:
- Configuration no longer matches what is in AWS
- Resources were renamed or recreated outside Terraform
- Console drift left stray objects in the VPC
Delete blockers in AWS first, then clear the VPC from state or retry destroy.
Terminate the target instance and wait until it disappears:
aws ec2 terminate-instances --instance-ids i-04d68e0520c9d0aa5 --region ap-south-1Wait until EC2 reports the instance as terminated:
aws ec2 wait instance-terminated --instance-ids i-04d68e0520c9d0aa5 --region ap-south-1wait exits silently when the instance reaches terminated. Detach and delete the internet gateway:
aws ec2 detach-internet-gateway --internet-gateway-id igw-0bf5307431a34eedc --vpc-id vpc-03cbdc25f0ea9da5f --region ap-south-1Delete the gateway after it is detached:
aws ec2 delete-internet-gateway --internet-gateway-id igw-0bf5307431a34eedc --region ap-south-1Delete the subnet and non-default security group:
aws ec2 delete-subnet --subnet-id subnet-0b3be385af2728630 --region ap-south-1Remove the security group once nothing references it:
aws ec2 delete-security-group --group-id sg-0a2a6e26ae449e908 --region ap-south-1If you deleted a subnet by hand earlier, an orphaned custom route table may remain — list and delete it before the VPC:
aws ec2 describe-route-tables --filters Name=vpc-id,Values=vpc-03cbdc25f0ea9da5f --region ap-south-1 --query 'RouteTables[?Associations[0].Main!=`true`].RouteTableId' --output textDelete any non-main route table ID the query returns:
aws ec2 delete-route-table --route-table-id rtb-0bbbaa5c3e76438d4 --region ap-south-1Finish with one of these:
terraform destroy— VPC still in state; review the plan, then typeyesaws ec2 delete-vpc+terraform state rm aws_vpc.pentest— abandoning the directory entirely
Manual teardown order (delete in this sequence):
- Parent services that create ENIs; terminate EC2 instances
- Wait until ENIs disappear from
describe-network-interfaces - NAT gateways, load balancers, and VPC endpoints (if present)
- Subnets (removes subnet–route-table associations), or disassociate custom route tables first if you keep the subnet
- Detach and delete internet or other gateways
- Non-default security groups
- Custom route tables
- VPC
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
DependencyViolation on aws_vpc during destroy |
Child resources still in AWS but missing from state, or out-of-band objects in the VPC | CLI dependency listing; terraform import children; or manual deletion in order above |
Still destroying... on VPC for many minutes |
AWS still reports dependencies on DeleteVpc | Stop the run; inspect ENIs and subnets; fix blockers before retrying |
| Destroy succeeds in state but VPC remains in console | Partial destroy or state rm on VPC only | Reconcile with terraform state list and aws ec2 describe-vpcs |
| Error persists after instance terminated | Lambda, ECS, or ELB managed ENI not yet released | Wait 10–20 minutes and retry; delete the parent service first |
Normal full destroy failed without state rm |
Console-created resource in the same VPC | Import or delete the stray object; tag VPC resources with Project for easier filtering |
For stage-by-stage debugging beyond VPC teardown, use Terraform troubleshooting.
Summary
When every child resource stays in state, terraform destroy removes instances, subnets, and gateways before the VPC and usually completes without DependencyViolation.
You see this error when:
- Terraform calls DeleteVpc while AWS still has subnets, ENIs, gateways, or security groups
- Child resources were removed from state with
terraform state rmbut left running in AWS - Console drift or another tool created objects inside the VPC that Terraform never tracked
Recovery in short:
- Confirm with
terraform state listand the VPC ID from the error orterraform output - Find blockers with
describe-network-interfacesand related CLI filters - Re-import children and run a full destroy, or delete AWS dependencies manually in order
terraform state rm stops Terraform from tracking a resource. It does not delete the real AWS object. Tag VPC resources with a consistent key (for example Project) so CLI filters stay simple when you hunt for strays.
References
- Amazon VPC — Delete a VPC
- AWS re:Post — Troubleshoot dependency errors when deleting a VPC
- HashiCorp Terraform — terraform destroy
- HashiCorp Terraform — State command
- HashiCorp Terraform AWS provider — aws_vpc
Frequently Asked Questions
Does a normal terraform destroy ever hit DependencyViolation on a fully managed stack?
Rarely, if every object was created by Terraform and nothing was removed from state.
- Terraform deletes instances before subnets
- Gateways detach before the VPC
- You see this error more often after
state rm, console drift, or resources another service created inside the VPC
Why does Terraform retry VPC deletion for several minutes?
The provider may retry while AWS still reports dependencies. You can see repeated Still destroying... lines before the run finally fails.
- Do not wait and hope the retries succeed
- Inspect ENIs and subnets, remove blockers, then retry destroy
Is terraform destroy -target=aws_vpc.main a shortcut?
No.
-targetselects the VPC and what the VPC depends on- It does not select subnets, instances, or other resources that depend on the VPC
- That leaves AWS dependencies in place and can cause
DependencyViolation
Use a normal full destroy when state is accurate. Reserve -target for exceptional recovery work.
Can I delete the default security group before the VPC?
No. AWS deletes the default security group when the VPC is deleted.
- Remove non-default security groups first
- An attached ENI can block deletion of a non-default group even when no instance is running
What if I already ran state rm and lost the resource IDs?
Try these in order:
- Filter by VPC ID and resource tags in the AWS CLI (
--filters Name=tag:Project,Values=your-project) - Read
terraform showfrom an older state backup file if you have one

