IaC Tips(yaml and json)
Separation of Concerns in IaC: Why We Pair YAML Templates with JSON Parameters
When designing scalable Infrastructure as Code (IaC), one of the earliest architectural hurdles engineers face is configuration sprawl. Hardcoding values directly into infrastructure templates quickly leads to code duplication and maintenance nightmares.
To solve this, a widely adopted industry best practice is the Separation of Concerns (SoC): utilizing YAML for the core infrastructure blueprint and JSON for the environment-specific parameters.
Here is a technical breakdown and practical guide on why this specific combination serves as an ideal design pattern for modern DevOps pipelines.
Why the Split? (YAML vs. JSON)
- YAML for Main Templates: Highly readable and human-centric. It uses indentation instead of noisy brackets, making complex, multi-resource architectures easy to audit. It also natively supports inline comments (
#) for documenting architectural decisions. - JSON for Parameters: Highly structured and machine-friendly. Continuous Integration and Continuous Deployment (CI/CD) runners (e.g., GitHub Actions, GitLab CI) and automated scripts can parse, validate, and inject dynamic variables into JSON much more reliably without worrying about whitespace sensitivity.
Optimized for minimal costs.
Mirrors production scale for testing.
High-availability enterprise tier.
Code Blueprint: The Architecture in Action
Instead of rewriting your code for every environment, your repository layout should remain modular. Let's look at a practical example using a standard cloud deployment:
📁 iac-infrastructure/
├── 📄 template.yaml <-- Core architecture logic (YAML)
├── 📄 params-dev.json <-- Development configuration (D)
├── 📄 params-staging.json <-- Staging configuration (S)
└── 📄 params-prod.json <-- Production configuration (P)
1. The Core Blueprint (template.yaml)
This file is written once. It defines what to build but leaves the specific details (like size and names) to the parameter files.
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Core Enterprise Web Server Infrastructure'
Parameters:
EnvPrefix:
Type: String
Description: 'Environment code prefix (e.g., D, S, P)'
InstanceType:
Type: String
Description: 'Compute instance size matching the environment budget'
Resources:
ApplicationServer:
Type: AWS::EC2::Instance
Properties:
# Automatically names the resource based on the environment (e.g., P-web-server)
Tags:
- Key: Name
Value: !Sub "${EnvPrefix}-web-server"
InstanceType: !Ref InstanceType
ImageId: ami-0c55b159cbfafe1f0
2. The Parameter Injections (params-*.json)
These files dictate the scale and environment identification. The deployment pipeline simply overlays the correct JSON onto the YAML blueprint.
Development (params-dev.json) — Low cost, small footprint.
[
{ "ParameterKey": "EnvPrefix", "ParameterValue": "D" },
{ "ParameterKey": "InstanceType", "ParameterValue": "t3.micro" }
]
Staging (params-staging.json) — Pre-production testing environment.
[
{ "ParameterKey": "EnvPrefix", "ParameterValue": "S" },
{ "ParameterKey": "InstanceType", "ParameterValue": "t3.medium" }
]
Production (params-prod.json) — High-availability, scaled footprint.
[
{ "ParameterKey": "EnvPrefix", "ParameterValue": "P" },
{ "ParameterKey": "InstanceType", "ParameterValue": "m5.large" }
]
Pro-Tips & Tricks for Managing Environments
When implementing this pattern in enterprise or homelab environments, keep these best practices in mind:
1. Enforce Standardized Single-Letter Prefixes
Using single-letter environment codes (D for Dev, S for Staging, P for Prod) keeps resource names clean, predictable, and short. This is incredibly helpful because cloud providers often have character limits on resource names or unique identifiers (like S3 buckets or global DNS records).
- Example:
P-core-dbvs.production-core-database-system
2. Implement CI/CD Dynamic Matching
Configure your deployment pipeline (like GitHub Actions) to automatically fetch the correct JSON file based on the Git branch you are merging into.
- Merging to
devbranch → Runstemplate.yaml+params-dev.json - Merging to
mainbranch → Runstemplate.yaml+params-prod.json
3. Use Parameter Validation Rules
To prevent an accidental deployment of a massive, expensive server instance into your Development environment, use AllowedValues or linting checks in your YAML to restrict what the JSON can inject:
# Inside template.yaml
InstanceType:
Type: String
AllowedValues: [t3.micro, t3.medium, m5.large]
Description: 'Enforces cost control and limits accidental over-provisioning.'
Conclusion
Leveraging YAML for structural declaration and JSON for data inputs strikes the perfect equilibrium between human readability and machine automation. It ensures your core infrastructure logic is thoroughly tested and remains static, while your environments scale dynamically and safely.

Comments
Post a Comment