Infrastructure as Code is mostly JSON underneath
You might write Terraform in HCL or a CloudFormation template in YAML, but underneath, both tools compile down to — and communicate in — JSON. Terraform's state file and plan output are JSON. CloudFormation templates can be written directly as JSON (YAML is just a friendlier syntax for the identical underlying structure). Knowing these shapes matters the moment you need to debug a broken deployment, write a script against terraform show -json, or generate a template programmatically.
Terraform state: JSON is the source of truth
Terraform's .tfstate file is JSON, and it's the single most important file in a Terraform-managed project — it's how Terraform knows what it already created, so it can compute a diff on the next plan.
{
"version": 4,
"terraform_version": "1.9.0",
"resources": [
{
"type": "aws_s3_bucket",
"name": "assets",
"instances": [
{
"attributes": {
"id": "my-app-assets-prod",
"bucket": "my-app-assets-prod",
"region": "us-east-1",
"arn": "arn:aws:s3:::my-app-assets-prod"
}
}
]
}
]
}Every resource Terraform manages gets an entry here with its real, current attributes as last known — this is why manually editing infrastructure outside Terraform ("ClickOps") causes drift: the state file's JSON no longer matches reality, and Terraform's next plan will try to "fix" changes you made by hand.
Reading a Terraform plan as JSON
terraform plan normally prints colored, human-readable output, but terraform show -json (on a saved plan file) emits the exact same information as structured JSON — essential for CI pipelines that need to programmatically check "does this plan delete a database?" before allowing it to apply:
terraform plan -out=tfplan
terraform show -json tfplan > plan.json{
"resource_changes": [
{
"address": "aws_db_instance.primary",
"type": "aws_db_instance",
"change": {
"actions": ["delete", "create"],
"before": { "instance_class": "db.t3.micro" },
"after": { "instance_class": "db.t3.large" }
}
}
]
}An actions array of ["delete", "create"] means Terraform intends to destroy and recreate that resource — often surprising and sometimes catastrophic for a database. Many teams script exactly this check: parse plan.json, scan resource_changes for a delete action on anything stateful, and fail the CI pipeline requiring manual approval before it can proceed.
CloudFormation templates: JSON or YAML, same shape
A CloudFormation template describes AWS resources declaratively. Written as JSON:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"AssetsBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": "my-app-assets-prod",
"VersioningConfiguration": { "Status": "Enabled" }
}
}
},
"Outputs": {
"BucketArn": { "Value": { "Fn::GetAtt": ["AssetsBucket", "Arn"] } }
}
}The same template in YAML is more concise but describes an identical structure — Resources, each with a Type and Properties, plus Outputs referencing them. CloudFormation itself parses YAML templates into this same internal JSON-like model before processing.
Terraform's own JSON syntax (not just HCL)
Fewer developers know Terraform configuration itself can be written as JSON instead of HCL — useful for generating configs programmatically rather than templating HCL strings by hand:
{
"resource": {
"aws_s3_bucket": {
"assets": {
"bucket": "my-app-assets-prod"
}
}
}
}Saved as main.tf.json, this is functionally identical to the equivalent HCL — Terraform accepts either, and tools that generate infrastructure config dynamically (a script building resources from a list) often emit JSON rather than assembling HCL as text.
Practical debugging workflows
- A plan looks wrong. Export it with
terraform show -jsonand diff thebefore/afterattributes for the resource in question using JSONKit's JSON Diff — far more precise than reading the colorized CLI summary for a resource with dozens of attributes. - State drifted. Pull the current state (
terraform state pull) and compare a resource's attributes against what your HCL actually declares. - Writing a template generator. Sketch the exact JSON shape you need to emit first (in the JSON Formatter), then write the code that produces it — designing the target shape before the generator avoids a lot of trial and error.
- Validating a hand-written JSON template. Run it through the JSON Validator before deploying — a trailing comma in a large CloudFormation JSON template is a common, easy-to-miss deploy blocker.