Infrastructure as Code Maturity: From Scripts to Declarative
Infrastructure as code has moved from a progressive practice to an enterprise expectation. The principle is well understood: infrastructure should be defined in version-controlled configuration files, provisioned through automated tooling, and managed with the same rigour applied to application code. The reality, however, varies enormously across organisations. Some teams have achieved fully automated, tested, and governed infrastructure management. Others are writing scripts that automate individual tasks without providing the reproducibility, auditability, and scalability that IaC promises.
The maturity spectrum is wide, and most enterprises contain teams at different points along it. The CTO’s challenge is to establish a target maturity level, provide the tooling and training to reach it, and create governance that ensures consistency without impeding the pace of infrastructure evolution.
Understanding where your organisation stands on this spectrum — and what investments will move it forward — is essential for any enterprise operating at cloud scale.
The Maturity Spectrum
Infrastructure automation maturity progresses through distinct levels, each building on the capabilities established by the previous.
Level one — Ad Hoc Scripting — is characterised by imperative scripts that automate individual provisioning tasks. Bash scripts that create cloud resources through CLI commands, Python scripts that call cloud provider APIs, and PowerShell scripts that configure Windows servers are typical. These scripts reduce manual effort but do not provide declarative state management, drift detection, or dependency resolution. Each script is a procedure that produces an outcome, but there is no mechanism to understand or enforce what the infrastructure should look like as a whole.
Level two — Configuration Management — introduces tools that manage the configuration state of existing infrastructure. Ansible, Chef, Puppet, and Salt provide declarative configuration for operating systems and applications, ensuring that servers maintain their intended state. These tools address configuration drift and enable consistent environment setup, but they typically operate at the server level rather than the infrastructure level. They assume that the underlying infrastructure (networks, load balancers, databases) is provisioned through other means.

Level three — Declarative Infrastructure Provisioning — introduces tools that manage the full infrastructure lifecycle declaratively. Terraform and Pulumi enable teams to define the desired state of their infrastructure — from networking through compute, storage, databases, and managed services — and the tooling determines the actions needed to reach that state. CloudFormation and Azure Resource Manager templates provide similar capabilities within their respective clouds. At this level, infrastructure changes follow a plan-apply workflow where the proposed changes are reviewed before execution, providing a safety net against unintended modifications.
Level four — Tested Infrastructure — applies software testing practices to infrastructure code. Unit tests validate that Terraform modules produce the expected resource configurations. Integration tests provision real infrastructure in an isolated environment and validate its behaviour. Compliance tests verify that infrastructure meets security and governance requirements. Tools like Terratest, kitchen-terraform, and Open Policy Agent (OPA) enable these testing patterns. At this level, infrastructure changes flow through a CI/CD pipeline that includes automated testing before deployment.
Level five — Policy-Governed Infrastructure — establishes automated governance that enforces organisational standards on all infrastructure provisioning. Policies defined in code (using OPA/Rego, HashiCorp Sentinel, or cloud-native policy services) are evaluated before infrastructure changes are applied. These policies can enforce security requirements (no public S3 buckets, encryption enabled on all databases), cost controls (instance size limits, required tagging), and architectural standards (approved regions, required networking configurations). Policy violations prevent the infrastructure change from being applied, shifting governance from review-after to prevent-before.
Tool Selection Strategy
The IaC tool landscape is mature enough that the primary tools are well-proven, but the selection decision still matters for enterprise adoption.
Terraform has established itself as the dominant multi-cloud IaC tool. Its provider model supports all major cloud platforms and hundreds of additional services, its state management provides drift detection and dependency tracking, and its module system enables reusable infrastructure components. The HashiCorp Configuration Language (HCL) is purpose-built for infrastructure definition and strikes a reasonable balance between readability and expressiveness. Terraform Cloud and Terraform Enterprise provide collaboration, governance, and state management capabilities designed for team and enterprise use.

Pulumi offers an alternative approach by enabling infrastructure definition in general-purpose programming languages — TypeScript, Python, Go, and C#. This appeals to teams that prefer the expressiveness and tooling ecosystem of programming languages over a domain-specific language. Pulumi provides the same declarative state management as Terraform but with the full capability of a programming language for complex logic, abstractions, and testing.
Cloud-native tools — CloudFormation for AWS, Azure Resource Manager for Azure, and Deployment Manager for GCP — provide deep integration with their respective platforms. They support all platform features immediately upon release and integrate natively with other cloud services. The trade-off is cloud lock-in: infrastructure code written for CloudFormation cannot manage Azure resources. For organisations committed to a single cloud provider, cloud-native tools are a viable choice. For multi-cloud environments, a provider-agnostic tool is essential.
For configuration management at the operating system level, Ansible has emerged as the most widely adopted tool, valued for its agentless architecture, YAML-based playbook syntax, and extensive module library. In a modern IaC stack, Ansible complements Terraform — Terraform provisions the infrastructure, Ansible configures the software running on it. For container-based deployments, this second layer may not be necessary if application configuration is fully containerised.
Module Architecture and Reuse
As IaC adoption matures, the organisation accumulates a library of infrastructure patterns that should be formalised as reusable modules. Module architecture is the IaC equivalent of software architecture — it determines how infrastructure code is organised, shared, and maintained.
Modules should encapsulate a coherent infrastructure component — a VPC with its subnets and route tables, a Kubernetes cluster with its node pools and networking, an RDS instance with its parameter groups and backup configuration. Each module exposes a well-defined interface (input variables and outputs) and encapsulates the implementation details.

A module registry — whether Terraform Cloud’s built-in registry, a private module registry, or a Git-based convention — provides discoverability and versioning. Module consumers pin to specific versions and upgrade deliberately, just as application developers pin library versions. This prevents upstream module changes from unexpectedly affecting downstream infrastructure.
Module testing is essential for modules that are shared across teams. A broken module can affect every consumer, making the blast radius of a defect proportional to the module’s adoption. Automated testing in the module’s CI/CD pipeline — including provisioning real infrastructure in a test account and validating its configuration — provides confidence that module updates are safe.
Governance and Organisational Patterns
Enterprise IaC governance must balance standardisation with autonomy. Teams need the flexibility to define the infrastructure their applications require while operating within organisational standards for security, cost, and architectural consistency.
The golden path model provides recommended patterns that teams are encouraged (but not required) to follow. The platform team publishes vetted Terraform modules for common infrastructure patterns — a standard web application stack, a standard database deployment, a standard Kubernetes namespace configuration. Teams that use these modules benefit from built-in compliance and operational maturity. Teams with non-standard requirements can build custom infrastructure, subject to policy validation.

The policy-as-code model automates governance enforcement. OPA/Rego policies, Sentinel policies, or cloud-native policy services evaluate proposed infrastructure changes against organisational rules. These policies are themselves version-controlled and reviewed, ensuring that governance evolves with organisational needs. The policy evaluation occurs in the CI/CD pipeline, providing immediate feedback to engineers rather than delayed feedback from a manual review process.
State management at enterprise scale requires careful attention. Terraform state contains sensitive information (resource identifiers, sometimes secrets) and must be stored securely with appropriate access controls. Remote state backends (S3 with DynamoDB locking, Terraform Cloud, Azure Storage with locking) provide team collaboration, concurrency control, and secure storage. State file isolation — separate state per environment, per team, or per application — limits the blast radius of state corruption and simplifies access control.
The journey from ad-hoc scripts to policy-governed declarative infrastructure is not merely a tooling upgrade — it is a maturation of how the organisation thinks about infrastructure. It is the transition from treating infrastructure as something built and maintained manually to treating it as software that is written, tested, reviewed, and continuously managed. For the CTO, this maturity directly enables the agility, reliability, and governance that cloud-native architecture demands.