Terraform JSON Configuration
Introduction
While Terraform typically uses its own configuration language called HCL (HashiCorp Configuration Language), it also supports JSON as an alternative format for defining infrastructure. This flexibility is particularly valuable when working with tools that can generate or manipulate JSON, or when integrating Terraform with other systems that speak JSON natively.
In this guide, we'll explore how to work with Terraform's JSON configuration format, understand its syntax compared to HCL, and learn practical scenarios where JSON configurations shine.
Why JSON Configuration?
Before diving into the details, let's understand why you might choose JSON over the standard HCL format:
- Programmatic generation: JSON is easier to generate programmatically from various languages and tools
- Existing tooling: Leverage existing JSON parsers, validators, and processors
- API integration: Seamless integration with REST APIs and services that use JSON
- Cross-platform compatibility: JSON is universally supported across multiple platforms and languages
JSON Configuration Basics
Terraform's JSON syntax is a direct mapping of HCL's structure but expressed in JSON format. Let's start with a simple example to illustrate the difference.
HCL vs JSON Comparison
Here's a basic HCL configuration:
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "example-instance"
Environment = "dev"
}
}
The equivalent JSON configuration would be:
{
"provider": {
"aws": {
"region": "us-west-2"
}
},
"resource": {
"aws_instance": {
"example": {
"ami": "ami-0c55b159cbfafe1f0",
"instance_type": "t2.micro",
"tags": {
"Name": "example-instance",
"Environment": "dev"
}
}
}
}
}
Notice how the JSON format maps directly to the HCL structure, but with explicit braces, quotes, and commas as required by JSON syntax.
Key Differences Between HCL and JSON
When working with Terraform's JSON format, keep these key differences in mind:
1. Block Structure
In HCL, you define blocks using a type-label syntax followed by curly braces:
resource "aws_instance" "example" {
// configuration here
}
In JSON, blocks are represented as nested objects with the block type as the key:
{
"resource": {
"aws_instance": {
"example": {
// configuration here
}
}
}
}
2. Expressions
HCL supports complex expressions:
count = var.instance_count > 0 ? var.instance_count : 1
In JSON, you need to use a special syntax for expressions:
{
"count": "${var.instance_count > 0 ? var.instance_count : 1}"
}
For more complex expressions, use the ${...}
syntax within JSON strings.
3. Multi-line Strings
HCL supports heredoc syntax for multi-line strings:
user_data = <<-EOT
#!/bin/bash
echo "Hello, World!"
service nginx start
EOT
In JSON, you need to use explicit newline characters:
{
"user_data": "#!/bin/bash
echo \"Hello, World!\"
service nginx start"
}
Converting Between HCL and JSON
Terraform provides built-in tools to convert between HCL and JSON formats.
HCL to JSON Conversion
To convert an HCL file to JSON:
terraform show -json terraform.tfstate > terraform.json
For configuration files, you can use:
terraform fmt -json main.tf > main.tf.json
JSON to HCL Conversion
Terraform doesn't provide a direct tool for JSON to HCL conversion, but you can use third-party tools like hcl2json
or json2hcl
.
Practical Example: Creating AWS Infrastructure with JSON
Let's create a more complex example that provisions an AWS VPC, subnets, and instances using JSON configuration.
{
"provider": {
"aws": {
"region": "us-east-1"
}
},
"resource": {
"aws_vpc": {
"main": {
"cidr_block": "10.0.0.0/16",
"tags": {
"Name": "MainVPC"
}
}
},
"aws_subnet": {
"public": {
"vpc_id": "${aws_vpc.main.id}",
"cidr_block": "10.0.1.0/24",
"availability_zone": "us-east-1a",
"tags": {
"Name": "PublicSubnet"
}
},
"private": {
"vpc_id": "${aws_vpc.main.id}",
"cidr_block": "10.0.2.0/24",
"availability_zone": "us-east-1b",
"tags": {
"Name": "PrivateSubnet"
}
}
},
"aws_instance": {
"web_server": {
"ami": "ami-0c55b159cbfafe1f0",
"instance_type": "t2.micro",
"subnet_id": "${aws_subnet.public.id}",
"user_data": "#!/bin/bash
apt-get update
apt-get install -y nginx
service nginx start",
"tags": {
"Name": "WebServer"
}
}
}
},
"output": {
"vpc_id": {
"value": "${aws_vpc.main.id}"
},
"web_server_ip": {
"value": "${aws_instance.web_server.public_ip}"
}
}
}
Save this as infrastructure.tf.json
and run Terraform commands as usual:
terraform init
terraform plan
terraform apply
Working with Variables in JSON
Variables work similarly in JSON, but with some syntax differences:
Variable Definitions
In HCL:
variable "instance_type" {
type = string
default = "t2.micro"
}
In JSON:
{
"variable": {
"instance_type": {
"type": "string",
"default": "t2.micro"
}
}
}
Variable Usage
In HCL:
resource "aws_instance" "example" {
instance_type = var.instance_type
}
In JSON:
{
"resource": {
"aws_instance": {
"example": {
"instance_type": "${var.instance_type}"
}
}
}
}
Modules in JSON Format
Modules can also be defined in JSON format. Here's an example:
{
"module": {
"vpc": {
"source": "terraform-aws-modules/vpc/aws",
"version": "3.14.0",
"name": "my-vpc",
"cidr": "10.0.0.0/16",
"azs": ["us-east-1a", "us-east-1b", "us-east-1c"],
"private_subnets": ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"],
"public_subnets": ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"],
"enable_nat_gateway": true,
"single_nat_gateway": true
}
}
}
Workflow Diagram
Here's a visualization of how Terraform processes JSON configuration files:
Real-World Use Cases
1. Infrastructure as Code Templating
When you need to generate infrastructure configurations dynamically:
{
"resource": {
"aws_instance": {
"__for_each": "${jsonencode(var.instances)}",
"each.key": {
"ami": "${each.value.ami}",
"instance_type": "${each.value.type}",
"tags": {
"Name": "${each.key}"
}
}
}
}
}
2. Integration with CI/CD Pipelines
JSON is often easier to generate and manipulate in CI/CD environments:
{
"terraform": {
"backend": {
"s3": {
"bucket": "my-terraform-state",
"key": "env/${ENV}/terraform.tfstate",
"region": "us-east-1"
}
}
}
}
3. API-Driven Infrastructure
When infrastructure is defined through API calls:
{
"data": {
"http": {
"config": {
"url": "https://api.example.com/infrastructure-config",
"request_headers": {
"Authorization": "Bearer ${var.api_token}"
}
}
}
},
"locals": {
"parsed_config": "${jsondecode(data.http.config.body)}"
}
}
Best Practices
- Consistent Format: Stick to either HCL or JSON for a project to avoid confusion
- Validation: Use JSON schema validation for your Terraform JSON files
- Version Control: Track changes with Git or other version control systems
- Documentation: Comment your JSON using infrastructure as code principles
- Generate Programmatically: Use tools to generate JSON rather than writing it manually
- Testing: Test your JSON configurations before applying them in production
Summary
Terraform's JSON configuration format provides a powerful alternative to HCL, particularly when working with tools that can generate or manipulate JSON. While it may be more verbose than HCL, its advantages in programmatic generation and integration with other systems make it a valuable option for advanced Terraform users.
Remember these key points:
- JSON configuration is a direct mapping of HCL structure
- Expression syntax differs slightly between HCL and JSON
- Terraform can convert between HCL and JSON formats
- JSON configuration is ideal for programmatic generation and API integration
Additional Resources
Exercises
- Convert an existing HCL configuration to JSON format
- Create a script that generates Terraform JSON configuration dynamically
- Build a multi-environment infrastructure using JSON configuration variables
- Integrate a Terraform JSON configuration with a CI/CD pipeline
- Create a complex module system using JSON configuration
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)