Fix the Network Module
A Terraform module for a VPC and its subnets broke in a refactor: it does not validate, its subnets overlap and it accepts any input. Fix it until its tests and random plans pass, with no AWS account.
The situation
Every team builds its VPC with the platform team's network module. Last sprint someone refactored it, and it has not worked since: terraform validate fails, and the private subnet of one availability zone still overlaps the public subnet of the next.
Subnets are created with count, so when a team removed eu-west-1a from a test stack, Terraform wanted to destroy and recreate every subnet. Nothing checks the inputs: last month someone created a VPC with a public /8 range. And a team that passed its own Name tag gave every resource in its VPC that one name.
Your task
- Validate the inputs: name (3 to 20 lowercase letters, digits and hyphens), environment (dev, staging or prod), vpc_cidr (a private IPv4 block from /16 to /24) and azs (2 to 4 different AZs in one region, ending in a to f).
- Create aws_vpc.this with DNS support and DNS hostnames, and one public and one private subnet per AZ, keyed by the AZ name.
- Split the VPC into 16 blocks. The AZ letter picks the public subnet's block (a is 0, f is 5), and the private subnet is 8 blocks higher, wherever the AZ is in the list.
- Tag every resource with the caller's tags plus Name, Environment, ManagedBy and, on subnets, Tier. The module's values win.
- Output the VPC ID, and maps from AZ to subnet ID and subnet CIDR block. The module has only aws_vpc and aws_subnet resources, may not read files or configure the AWS provider, and reads only id, cidr_block, availability_zone and vpc_id from its resources.
What the grader checks
| Check | Passes when |
|---|---|
| Formatted and valid | The module has only variable, locals, resource (aws_vpc and aws_subnet) and output blocks, reads no files or paths, calls no provider functions, and reads only id, cidr_block, availability_zone and vpc_id from its resources. terraform init works with no network and only the AWS provider, terraform validate passes, and terraform fmt has nothing to change in main.tf, variables.tf and outputs.tf. |
| The module's tests pass | terraform test passes every run in tests/network.tftest.hcl, with a mock AWS provider. One run gives the VPC and subnets known IDs to check the outputs and that every subnet is in the VPC. |
| Inputs are validated | For every input rule in the brief, a random value that breaks it (20 in all) is rejected by a variable validation rule, and three random sets of good inputs plan without errors. |
| Subnets for random inputs | For three random sets of good inputs, the plan has aws_vpc.this with DNS turned on, and one public and one private subnet per AZ in that VPC, keyed by AZ name, with the range the AZ letter picks. The outputs map AZs to subnet ranges and IDs, checked with random mocked IDs in a test written for each run. |
| Tags | In the same plans, the VPC and every subnet have the caller's tags plus Name, Environment, ManagedBy and, on subnets, Tier from the module, and the module's values win. |
When a check fails, the grader's comment says what it saw, not how to fix it.
Files you may change
challenges/fix-the-network-module/main.tfchallenges/fix-the-network-module/variables.tfchallenges/fix-the-network-module/outputs.tf
A pull request that changes any other file, including the tests or the workflows, is not graded.
Hints
Start with what Terraform tells you
Run terraform validate and read each error. It names the file and the line.
count or for_each
Resources created with count are known by their position in the list, so removing the first AZ shifts every other subnet. Resources created with for_each are known by their key.
From AZ letter to block number
Look at substr(), index() and cidrsubnet() in the Terraform function documentation.
Validation rules that do not crash
cidrhost() fails on a value that is not a CIDR block. can() turns that failure into false. A variable can have more than one validation block, each with its own error message.
Which tag wins
merge() keeps the value from the last map that has the key.
Test it locally
From the challenge folder, with Terraform 1.11 or later. You do not need an AWS account: the tests use a mock provider.
terraform init
terraform validate
terraform fmt -check
terraform testSubmit
- Fork The-DevOps-Daily/challenges and create a branch.
- Commit your changes to the files listed above, and nothing else.
- Open a pull request against main in The-DevOps-Daily/challenges.
- Read the grader's comment. If a check fails, push another commit to the same branch and it grades again.
- When every check passes, the PR gets the passed label and is closed. It is never merged, and your pass is usually counted on this page within a few minutes.
The workflow takes the tests and the grader from the repository's main branch and copies only the files above out of your PR. Your code runs in a container or a throwaway kind cluster, and some inputs are random at grading time. A second workflow checks the result against your PR's real commit before it comments.
Learn the basics first
Exercise
AWS VPC with Terraform
Build a complete AWS VPC infrastructure using Terraform with public/private subnets, NAT gateway, and security groups.
120 minutes
Exercise
Deploy a DigitalOcean Droplet with Terraform
Learn Infrastructure as Code basics by deploying a simple web server on DigitalOcean using Terraform. Perfect for beginners starting their IaC journey.
60 minutes
Guide
Introduction to Terraform
Learn Terraform 1.17 step by step: install it, write HCL, manage state, use variables and modules, set up S3 remote state with locking, and test your code.
Guide
Networking Fundamentals for Developers
Learn networking in a lab you build on one Linux VM: TCP/IP, subnets, DNS, firewalls, load balancing, Docker, Kubernetes, VPCs and troubleshooting.
We earn commissions when you shop through the links below.
More challenges
Docker · Starter
Shrink the Image
A tiny Flask app ships in a container image of more than 1 GB that runs as root. Rewrite the Dockerfile so the image is under 200 MB, runs as a normal user and still works.
About 30 min
Bash · Starter
Script It: Summarize an Access Log
Finish a half-written Bash script that turns an nginx access log into a short report: requests, status classes, top clients and top paths. ShellCheck has to be clean and the edge cases right.
About 45 min
Kubernetes · Intermediate
Fix the Deployment
An inventory API never comes up on Kubernetes. Find and fix the bugs in its Deployment, Service and ConfigMap until the rollout finishes and the Service answers.
About 45 min
PostgreSQL · Intermediate
Speed Up the Slow Queries
Three queries behind the shop's busiest pages read whole tables. Add the right PostgreSQL indexes, and fix the query an index cannot help, so each one reads a few pages and returns the same rows.
About 45 min
Prometheus · Intermediate
Write the Alerts
The checkout service has metrics but no useful alerts. Write four Prometheus alert rules that page for real outages and stay quiet for blips, quiet nights and other services.
About 45 min