How I Built VisualTF: Catching Terraform Misconfigurations Before Code is Written
Platform teams spend a lot of time reviewing the same infrastructure mistakes in pull requests. Two subnets in a virtual network accidentally share overlapping CIDR blocks. A storage account or database gets provisioned with public network access enabled because someone forgot to wire up a Private Endpoint. A subnet goes out without a Network Security Group attached.
Tools like tflint and policy checks in CI help catch some of these issues, but by the time a pull request is open, the engineer has already written all the Terraform code. Fixing a broken network layout at that stage means tearing up files and starting over.
I built VisualTF to move those checks to design time. You sketch your Azure architecture on a canvas in the browser, and it validates your topology rules while compiling the diagram into clean Terraform HCL in real time.
Representing Cloud Infrastructure as a Directed Graph
Under the hood, a Terraform configuration is really a dependency graph. A Subnet depends on a Virtual Network. A Private Endpoint depends on both the target PaaS resource and the Subnet it attaches to.
In VisualTF, every node dropped onto the canvas is stored as a typed node in a graph, and every line drawn between two nodes creates a directed edge. Because I have a computer science background, modeling this as an in-memory graph in TypeScript felt natural. Whenever you add a node, connect two resources, or edit a property in the sidebar, the app runs two fast passes over the graph:
- A validation pass that checks security and networking rules across connected nodes.
- A codegen pass that walks the graph in topological order and emits
azurermTerraform blocks.
Everything runs in the browser with zero backend servers and no cloud credentials required.
Catching Overlapping CIDRs with Bitwise Math
One of the most annoying Azure errors to hit during terraform apply is an overlapping subnet CIDR block. Humans are not great at looking at 10.0.1.0/24 and 10.0.0.0/23 and immediately spotting that the second range swallows the first one.
In VisualTF, every time a subnet CIDR changes, the validator converts each IPv4 address and prefix length into 32-bit unsigned integers:
function parseCidrRange(cidr: string): { start: number; end: number } | null {
const [ip, prefixStr] = cidr.split("/");
const prefix = Number(prefixStr);
const octets = ip.split(".").map(Number);
if (octets.length !== 4 || isNaN(prefix) || prefix < 0 || prefix > 32) {
return null;
}
const ipInt =
((octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3]) >>> 0;
const mask = prefix === 0 ? 0 : (~0 << (32 - prefix)) >>> 0;
const start = (ipInt & mask) >>> 0;
const end = (start | (~mask >>> 0)) >>> 0;
return { start, end };
}
function cidrsOverlap(cidrA: string, cidrB: string): boolean {
const rangeA = parseCidrRange(cidrA);
const rangeB = parseCidrRange(cidrB);
if (!rangeA || !rangeB) return false;
return rangeA.start <= rangeB.end && rangeB.start <= rangeA.end;
}
With that check running on every keystroke, the canvas highlights conflicting subnets in red the moment you type an overlapping range, and it also verifies that every subnet range actually sits inside its parent VNet's address space.
Enforcing Security Guardrails by Default
Beyond networking math, the validation engine checks for common operational risks across the graph:
- Publicly exposed PaaS resources: If you drop an Azure Key Vault, Storage Account, or SQL resource onto the canvas with public access enabled and no Private Endpoint edge connected, VisualTF flags it right away.
- Unprotected subnets: Subnets without an attached Network Security Group trigger a warning so traffic filtering is never an afterthought.
- TLS and SKU baselines: Resources default to TLS 1.2 minimums and production-safe SKUs in the generated HCL.
Compiling the Graph into Idiomatic HCL
A lot of visual cloud tools generate messy code full of hardcoded strings that no SRE would ever merge. For VisualTF to actually be useful, the generated Terraform had to look like code I would write by hand.
When the compiler walks the graph, it resolves edges into real Terraform resource references. Instead of outputting a literal string for resource_group_name or virtual_network_name, it emits azurerm_resource_group.main.name and azurerm_virtual_network.main.name so Terraform's dependency graph matches what you drew on the canvas.
What is Next
Azure support is live today at visual-tf.vercel.app. Next on my list is supporting custom reusable module outputs alongside raw azurerm resources, followed by AWS and GCP provider targets.