E-Library Logo
Infrastructure as Code (IaC) with Terraform/Ansible
Infrastructure as Code (IaC) with Terraform/Ansible
Infrastructure as Code (IaC) with Terraform/Ansible illustration

Infrastructure as Code (IaC) with Terraform/Ansible

Program: Post Graduate Program in Dev Ops
Program Level: Post Graduate
Language: English
Subject Syllabus

Subject Overview

An advanced study of Infrastructure as Code using Terraform and Ansible, covering declarative infrastructure provisioning, configuration automation, testing, security, governance, delivery pipelines, cloud operations and enterprise-scale transformation. The subject combines technical practice with architecture, analytics, risk management and strategic decision-making.

Learning Goal

Students will be able to design, implement, test, secure and govern production-grade Infrastructure as Code solutions; integrate Terraform and Ansible into DevOps workflows; evaluate architectural and operational trade-offs; and lead scalable, ethical and cost-conscious infrastructure automation initiatives.

Syllabus

1. Infrastructure as Code Foundations

Learning Objective: Explain IaC terminology, evolution, benefits, limitations and principles such as idempotency, immutability, declarative versus imperative automation, desired state and reproducibility. Compare manual provisioning, scripts, configuration management and infrastructure provisioning tools.

Category: foundation

2. IaC Architecture and Delivery Lifecycle

Learning Objective: Model the IaC lifecycle from requirements and architecture through coding, review, deployment, operation and retirement. Apply source control, environment promotion, branching, documentation and infrastructure design principles.

Category: foundation

3. Terraform Fundamentals and HCL

Learning Objective: Use Terraform CLI workflows and HashiCorp Configuration Language to define providers, resources, data sources, variables, outputs, expressions, dependencies and lifecycle rules. Interpret execution plans and apply changes safely.

Category: core

4. Terraform State and Resource Lifecycle

Learning Objective: Manage local and remote state, locking, backends, resource addressing, imports, moves and state recovery. Analyze state security, drift, dependency graphs and replacement risks in collaborative environments.

Category: core

5. Reusable Terraform Architecture

Learning Objective: Design reusable modules, composition patterns, naming conventions, version constraints and provider configurations. Evaluate workspaces, repository structures and multi-account or multi-subscription deployment models.

Category: core

6. Ansible Foundations and Inventory Management

Learning Objective: Configure Ansible control nodes, inventories, variables, facts, modules, ad hoc commands and connection methods. Explain agentless automation, execution models, precedence rules and idempotent configuration management.

Category: core

7. Ansible Playbooks, Roles and Advanced Automation

Learning Objective: Develop playbooks using tasks, handlers, templates, loops, conditions, tags, blocks, error handling, roles, collections and dynamic inventories. Create reusable and maintainable automation for operating systems, middleware and applications.

Category: core

8. Integrating Terraform and Ansible

Learning Objective: Combine Terraform provisioning with Ansible configuration while maintaining clear tool boundaries. Implement inventory hand-offs, outputs, orchestration workflows and dependency management, and assess alternatives such as cloud-init and image-based provisioning.

Category: application

9. Cloud, Containers and Hybrid Infrastructure

Learning Objective: Apply IaC to public cloud, private cloud, networking, identity, storage, managed services, Kubernetes and hybrid infrastructure. Evaluate portability, provider-specific capabilities, immutable images and multi-cloud trade-offs.

Category: application

10. Testing, Quality and Reliability Engineering

Learning Objective: Apply formatting, validation, linting, static analysis, unit, contract, integration and end-to-end testing. Use Terraform test frameworks, Ansible testing tools, policy checks and ephemeral environments to improve reliability and recoverability.

Category: application

11. Security, Secrets and Policy as Code

Learning Objective: Implement least privilege, secure state, secret management, encryption, supply-chain controls and module or collection provenance. Apply policy as code, compliance automation, vulnerability scanning and secure coding practices without exposing sensitive data.

Category: strategic

12. CI/CD, GitOps and Platform Engineering

Learning Objective: Build automated IaC pipelines with plan, review, approval, apply, rollback and audit stages. Relate IaC to GitOps, internal developer platforms, self-service infrastructure, golden paths and progressive delivery.

Category: strategic

13. Operations, Governance and Infrastructure Analytics

Learning Objective: Manage drift detection, observability, incident response, disaster recovery, upgrades, decommissioning and technical debt. Use deployment, failure, lead-time, compliance, utilization and cost metrics to support capacity planning, FinOps and risk-based decisions.

Category: strategic

14. Enterprise Strategy, Ethics and Emerging IaC

Learning Objective: Develop adoption roadmaps, operating models, standards, governance controls and business cases for digital transformation. Examine skills, vendor lock-in, sustainability, accountability and ethical automation, while evaluating AI-assisted coding, autonomous remediation, intent-based infrastructure and evolving provisioning technologies through enterprise case analysis.

Category: contemporary

Infrastructure as Code (IaC) with Terraform/Ansible
Post Graduate Program in Dev Ops

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 1
Infrastructure as Code Foundations

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

1. Infrastructure as Code Foundations

Introduction and Context

Infrastructure as Code (IaC) is the practice of defining, provisioning, configuring, and managing computing infrastructure through machine-readable files and automated processes. Instead of creating servers, networks, databases, access policies, and application platforms manually through web consoles or command-line instructions, an organization describes the intended infrastructure in code. Tools then interpret that description and create or modify resources in a controlled manner.

IaC has become a foundational practice in DevOps because modern systems are distributed, frequently changed, and often deployed across multiple environments. A development platform may contain virtual machines, containers, managed databases, load balancers, identity policies, monitoring services, and network controls. Managing such systems manually creates inconsistency and makes it difficult to determine who changed what, when the change occurred, and whether the same environment can be recreated elsewhere. IaC addresses these problems by treating infrastructure definitions as versioned, reviewable, testable, and repeatable artifacts.

Terraform and Ansible illustrate two closely related but distinct uses of IaC. Terraform is primarily an infrastructure provisioning and orchestration tool. It is commonly used to create cloud resources, networks, storage, identity objects, and other infrastructure components. Ansible is primarily an automation and configuration-management tool. It is commonly used to install packages, configure operating systems, deploy applications, and enforce system settings. In practice, the tools may be used together, but their operating models and strengths differ.

IaC is not merely a technique for replacing manual work with scripts. It represents a change in the way infrastructure is designed, governed, documented, and operated. It introduces software-engineering ideas such as version control, code review, testing, modularity, dependency management, and continuous integration into infrastructure management.

Learning Objectives

After studying this chapter, students should be able to:

1. Define Infrastructure as Code and explain its role in DevOps.

2. Distinguish provisioning, configuration management, orchestration, and deployment automation.

3. Explain idempotency, immutability, desired state, drift, reproducibility, and declarative automation.

4. Compare manual provisioning, scripts, configuration-management tools, and infrastructure-provisioning tools.

5. Describe the evolution of infrastructure automation and the roles of Terraform and Ansible.

6. Evaluate the benefits, limitations, risks, and governance requirements of IaC.

7. Select an appropriate automation approach for a given organizational situation.

8. Apply IaC principles to a realistic infrastructure-management scenario.

From Hand-Built Servers to Programmable Infrastructure

Infrastructure management has developed through several broad stages. In the earliest stage, administrators installed operating systems, configured networks, and deployed applications manually. Documentation was often informal, and the exact sequence of actions depended on individual experience. This approach could work for a small number of stable servers, but it became unreliable as systems grew.

The next stage involved shell scripts and other procedural automation. A script could install software, create directories, modify configuration files, and start services. Scripts reduced repetitive effort and captured operational knowledge. However, many scripts described a sequence of commands rather than the desired final condition. Re-running a script might duplicate entries, overwrite an intentional change, or fail because an earlier command had already been performed. Error handling and platform portability also became difficult.

Configuration-management tools introduced a more systematic approach. Tools such as Ansible, Puppet, and Chef allow administrators to express configuration requirements and apply them across multiple systems. Their central concern is usually the state of an existing machine: which packages are installed, which services are running, which users exist, and which configuration values are present.

Cloud computing created a further need. Infrastructure could now be created through APIs in seconds, but the number and variety of resources increased dramatically. Organizations needed to create complete environments consisting of networks, subnets, routing rules, security groups, compute instances, databases, and monitoring components. Infrastructure-provisioning tools such as Terraform address this requirement by managing resource lifecycles through code and dependency graphs.

The current stage combines IaC with version control, automated testing, policy enforcement, continuous integration and continuous delivery, observability, and self-service platforms. Infrastructure is increasingly treated as a product: it has owners, users, interfaces, quality standards, security requirements, and a lifecycle.

A Vocabulary for Infrastructure as Code

Several terms must be distinguished carefully.

Infrastructure provisioning is the creation, modification, and deletion of infrastructure resources such as virtual networks, virtual machines, databases, storage volumes, and cloud services.

Configuration management is the process of ensuring that a system has the required software, settings, users, permissions, files, and services.

Orchestration coordinates multiple operations or systems in a defined order. For example, an orchestration process may create a network, provision a database, deploy application servers, and then configure a load balancer.

Deployment is the release of an application, service, or configuration into a target environment. Infrastructure provisioning may be necessary before deployment, but the two activities are not identical.

Desired state is the condition an organization intends a system or resource to have. Examples include “three application instances must exist,” “the database must not be publicly accessible,” or “the Nginx service must be running.”

Actual state is the condition currently observed in the environment. The difference between desired and actual state is often called drift.

Idempotency means that applying an operation repeatedly produces the same intended result as applying it once. An idempotent automation task checks the current condition and changes it only when necessary.

Immutability is the practice of replacing an infrastructure artifact or instance rather than modifying it in place. An immutable server is built from a known image or definition and replaced when a change is required.

Reproducibility is the ability to recreate an equivalent environment from the same source definitions, inputs, and dependencies.

Declarative automation specifies what the desired result should be. The tool determines the actions needed to reach that result.

Imperative automation specifies how to perform a sequence of actions. The author controls the steps and their order.

State is information maintained by an automation system about resources it manages. Terraform, for example, uses state to associate configuration with real infrastructure objects and to calculate changes.

Drift detection is the identification of differences between declared configuration and the environment that actually exists.

Declarative and Imperative Ways of Thinking

The distinction between declarative and imperative automation is central to IaC. Consider a requirement that a web server package must be installed and its service must be running. An imperative script may execute commands such as updating package metadata, installing the package, editing a file, and starting the service. The script describes the route to the result.

A declarative configuration instead states that the package must be installed and the service must be enabled and running. The automation engine examines the current system and performs only the actions necessary to satisfy the requirement. This separation between intention and implementation allows the tool to handle different starting conditions more safely.

Declarative automation does not eliminate procedural logic. Conditions, loops, dependencies, handlers, and ordering may still be required. Rather, it changes the primary abstraction from a command sequence to a target state. Terraform is strongly declarative: a configuration describes resources and relationships, while Terraform constructs an execution plan. Ansible supports declarative patterns through modules and desired-state tasks, although playbooks can also contain procedural ordering and conditional behavior.

Neither model is universally superior. Imperative scripts are useful for small, specialized, one-time operations and situations where exact procedural control is necessary. Declarative systems are generally more suitable for repeatable environment management, but they require disciplined state handling and an accurate model of resource behavior.

Core Principles of IaC

Idempotency and Convergence

Idempotency allows an automation process to be safely repeated. If a task declares that a directory should exist, running it once creates the directory and running it again confirms that it already exists. It should not create a second directory or fail unnecessarily. Idempotency is particularly important in CI/CD pipelines, recovery procedures, and large-scale operations where the same automation may be triggered more than once.

A related concept is convergence. A convergent system progressively moves the actual environment toward the desired state. If a configuration has drifted, the next application of automation should correct the drift, subject to the tool’s permissions and resource behavior.

Idempotency is not automatic. A command that appends a line to a file on every execution is not idempotent. A task that creates a database without checking whether it already exists may fail or create unintended resources. Engineers must choose appropriate modules, conditions, resource identifiers, and lifecycle settings.

Desired State and Drift Management

A declared configuration is a model of intent, not necessarily a complete description of every property in the environment. Teams must decide which attributes are controlled by IaC and which are managed elsewhere. If a cloud resource is changed manually, the environment may drift from the declared model. Drift can produce security weaknesses, unexpected costs, failed deployments, or misleading documentation.

Drift management involves detecting differences, determining whether they are accidental or intentional, and deciding how to reconcile them. The correct response may be to apply the code, update the code, or formally preserve an externally managed exception. Automatic correction is not always safe; an apparently unwanted change may represent an urgent operational fix.

Immutability and Replaceable Infrastructure

Mutable infrastructure is changed in place. This can be efficient for minor adjustments, but over time the server may acquire undocumented packages, temporary files, and manual fixes. Such accumulated variation is sometimes described as configuration drift or server snowflake behavior.

An immutable approach builds a new image or instance from a known definition and replaces the old one. This improves consistency and rollback when combined with versioned artifacts and health checks. It may, however, increase deployment complexity and require careful handling of stateful systems. Databases, persistent storage, and long-lived connections cannot always be replaced as easily as stateless application servers.

Reproducibility and Determinism

Reproducibility requires more than storing a configuration file. Provider versions, operating-system images, package repositories, modules, secrets, environment variables, and external APIs may all affect the result. Dependencies should therefore be versioned or constrained where practical, and configurations should avoid uncontrolled sources of variation.

A reproducible environment is not necessarily bit-for-bit identical. Cloud platforms may assign different IP addresses or hardware, and timestamps or generated identifiers may differ. The important requirement is that the environment conforms to the relevant functional, security, and operational specifications.

Versioning and Reviewability

IaC files should normally be stored in a version-control system. A change can then be reviewed, attributed, compared with earlier versions, and reverted when necessary. Pull requests and peer review provide an opportunity to examine security groups, identity permissions, network exposure, cost implications, and destructive operations before execution.

Treating infrastructure code like application code does not mean using identical testing methods. Infrastructure changes require additional attention to provider APIs, dependency graphs, state transitions, failure recovery, and environmental differences.

Terraform and Ansible in the IaC Landscape

Terraform uses provider plugins to communicate with infrastructure platforms. A Terraform configuration describes resources, data sources, variables, outputs, and relationships. Terraform typically compares the configuration and its state information with the remote environment, creates a plan, and applies the required changes. Its resource graph helps establish dependencies and determine an execution order.

Terraform is well suited to creating and managing relatively durable infrastructure resources. Examples include virtual private networks, subnets, security groups, compute instances, managed databases, DNS records, and cloud storage. Its state is operationally important and must be protected, backed up, and shared appropriately in team environments. Remote state storage, locking, access control, and state separation are common practices.

Ansible generally connects to target hosts or services and executes modules through playbooks. A playbook can install packages, manage files, create users, configure services, apply templates, and perform application deployment tasks. Ansible is agentless in its common system-management model, using remote communication such as SSH or WinRM. It is particularly useful when machines already exist and must be configured consistently.

A common division of responsibility is to use Terraform to provision the underlying platform and Ansible to configure operating systems and applications. This is not a universal rule. Terraform can invoke other systems, and Ansible can interact with cloud APIs, but combining tools requires clear ownership boundaries. Otherwise, two tools may attempt to manage the same property and produce conflicts.

Comparing Automation Approaches

Manual provisioning offers flexibility and immediate human judgment. It is useful for experimentation, emergency intervention, and unfamiliar environments. Its weaknesses include slow execution, inconsistent results, weak auditability, dependence on individual knowledge, and difficulty reproducing a previous environment.

Ad hoc scripts are inexpensive to create and powerful for narrowly defined operations. They become risky when they lack error handling, input validation, idempotency, logging, testing, or documentation. A script may be an appropriate component of an automation system, but a collection of ungoverned scripts is not automatically IaC.

Configuration-management tools are designed to maintain the state of systems that already exist. They excel at operating-system and application configuration, package management, file templating, service control, and repeated enforcement. Their challenge is that they may be less suitable for modelling complex infrastructure dependencies and resource lifecycles.

Infrastructure-provisioning tools model infrastructure resources and their relationships. They provide a consistent interface to platforms and can create complete environments from code. Their challenges include state management, provider differences, asynchronous APIs, destructive changes, and the possibility that a declarative model may not capture every operational nuance.

The choice should be based on the problem rather than tool popularity. Important questions include: Is the target a host or a platform? Is the operation repeatable? Must the environment be recreated? Who owns the resource? Is there persistent data? How will changes be reviewed? What is the failure and rollback strategy?

Practical Workflow for IaC Adoption

A mature IaC workflow commonly begins with inventory and ownership. Teams identify resources, dependencies, service owners, compliance requirements, and existing manual procedures. They then select a source-of-truth model and define which resources will be managed by which tool.

The next stage is modelling. Resources are divided into coherent modules or roles with clear inputs and outputs. Naming, tagging, identity, network boundaries, backup policies, and logging requirements should be designed rather than added as afterthoughts.

Changes are then developed in version control and validated through formatting checks, static analysis, unit or module tests, policy checks, and safe planning. A review process examines not only syntax but also blast radius, cost, security, data retention, and recoverability.

Execution should use controlled credentials, protected state, environment-specific variables, and appropriate approvals. After deployment, monitoring and drift detection provide feedback. Failed changes require a recovery process, which may include reverting code, restoring state, rolling back an image, or applying a corrective configuration.

Applied Case: Rebuilding a Multi-Environment Web Platform

Background

The following is an illustrative case. A growing financial-services organization operates a customer web platform in development, testing, and production environments. Development infrastructure is created by engineers through a cloud console. Test servers are built from scripts maintained by different teams, while production changes require a small operations group to perform manual procedures.

Situation and Problem

The environments differ in operating-system packages, network rules, and monitoring configuration. A defect appears in production but cannot be reproduced in testing. A former employee’s undocumented console changes also make it difficult to determine why a database is publicly reachable. Provisioning a new test environment takes several days, and the organization cannot reliably estimate the cost of temporary environments.

Analysis

The problem is not simply slow provisioning. The organization lacks a common model of desired state, consistent ownership, and an auditable change process. Manual actions and scripts have created drift. Application servers are mutable, and the existing scripts are not reliably idempotent. In addition, database resources contain persistent data and therefore require a different lifecycle from stateless web servers.

Approach and Strategy

The organization first inventories resources and separates responsibilities. Terraform is selected to model networks, subnets, firewall rules, load balancers, managed databases, and compute resources. Ansible is selected for operating-system hardening, package installation, service configuration, and deployment preparation.

The team stores code in a version-control repository and creates reusable modules for network and application-platform components. Environment-specific values are supplied through controlled variables rather than copied configuration files. State is stored remotely with access controls and locking. Production changes require peer review and an approval step.

Application servers are rebuilt from a versioned image or consistent provisioning process rather than modified indefinitely in place. Database deletion is restricted, backups are required, and replacement policies are used cautiously. Automated checks verify that production databases cannot receive unrestricted public traffic and that required logging is enabled.

Before adopting the new workflow for production, the team imports or recreates existing resources carefully, compares actual settings with the intended model, and resolves ownership conflicts. Ansible tasks are written to be idempotent: packages are declared by state, configuration files are rendered from templates, and services are enabled and started only when required.

Outcome

New environments can be created from reviewed definitions, and differences between environments become visible in code. The organization gains a reliable audit trail and can identify whether a change was intentional. Engineers can reproduce many production conditions in testing, although data-handling controls prevent copying sensitive customer data directly. The team also discovers that IaC does not remove the need for architecture, operations expertise, backups, or incident response.

Learning Points

The case demonstrates that IaC is an organizational control system as much as a technical mechanism. Tool selection must follow resource ownership and lifecycle. Stateless infrastructure can often be replaced, while stateful resources require preservation and explicit safeguards. Finally, consistency depends on governance, review, security, and monitoring in addition to code.

Risks, Limitations, and Common Mistakes

IaC can amplify mistakes. A defective network rule or deletion instruction may affect an entire environment. Automation makes changes faster, but speed without review can increase blast radius. Destructive operations should therefore be visible in plans and protected through approvals, lifecycle rules, backups, and separation of duties.

Secrets should not be embedded in repositories, plain-text variables, logs, or state files without appropriate protection. State may contain identifiers, connection details, or sensitive attributes even when configuration files do not. Access to repositories, execution systems, cloud accounts, and state storage must be controlled and audited.

Another common error is managing the same resource from multiple sources. For example, a security-group rule may be declared in Terraform, changed by a console user, and partially overwritten by a separate script. Ownership boundaries should be explicit.

Teams also underestimate import and migration work. Existing infrastructure cannot always be adopted safely by writing code and immediately applying it. The declared model must first be compared with the actual resource, and unexpected differences must be understood.

Over-modularization is another problem. Excessive abstraction can hide important relationships and make troubleshooting difficult. Modules and roles should represent stable, meaningful boundaries rather than every small resource.

IaC does not guarantee reliability. Provider bugs, API outages, quota limits, race conditions, incorrect dependencies, invalid assumptions, and human approval errors remain possible. Testing should include failure scenarios, not only successful creation.

Governance, Ethics, and Professional Responsibility

Infrastructure decisions affect confidentiality, integrity, availability, cost, and sometimes human safety. IaC code can create excessive privileges, expose personal data, disable encryption, or remove audit controls. Therefore, infrastructure engineers have professional responsibilities beyond making deployments succeed.

Governance should address identity and access management, separation of duties, approval requirements, retention and deletion, logging, regulatory obligations, data residency, backup, and emergency change procedures. Policy-as-code can automatically test selected controls, but automated policy checks must themselves be maintained and validated.

Organizations should also consider the environmental and financial effects of infrastructure. Unused development environments, overprovisioned instances, and unnecessary data replication consume resources and increase cost. IaC enables tagging, budgets, schedules, and standardized sizing, but these controls require organizational ownership.

Current Developments and Emerging Directions

IaC is increasingly integrated with platform engineering. Internal developer platforms expose approved infrastructure capabilities through templates and service catalogs, allowing developers to obtain environments without learning every provider-specific detail. This creates productivity benefits but also makes platform governance and interface design important.

Policy-as-code systems evaluate infrastructure definitions before deployment. Security scanning, cost estimation, compliance validation, and dependency analysis are becoming routine parts of infrastructure pipelines. Drift detection and continuous reconciliation are also expanding, particularly in cloud-native and Kubernetes environments.

Artificial intelligence is being used to suggest configurations, explain plans, detect anomalies, and assist with troubleshooting. Such tools can accelerate work, but generated infrastructure must be reviewed for security, cost, correctness, licensing, and hidden assumptions. AI assistance does not replace accountable human approval.

The boundary between provisioning, configuration, and application delivery is also becoming less distinct. Containers, Kubernetes operators, GitOps controllers, and managed cloud services introduce additional declarative layers. The fundamental principles remain the same: define ownership, understand state, control change, make outcomes reproducible, and design safe recovery.

Best Practices for Professional IaC

1. Store infrastructure definitions in version control and review significant changes.

2. Define clear ownership for every resource and avoid overlapping management tools.

3. Prefer idempotent modules, tasks, and resource definitions.

4. Pin or constrain important tool, provider, module, image, and dependency versions.

5. Protect state files, credentials, and execution identities.

6. Use plans, validation, policy checks, testing, and approvals before high-impact changes.

7. Separate environments and use consistent naming, tagging, and access controls.

8. Design backups and recovery before automating destructive operations.

9. Treat persistent data differently from replaceable compute resources.

10. Monitor for drift, failed runs, unexpected cost, and security violations.

11. Keep modules understandable and document assumptions, inputs, outputs, and ownership.

12. Test not only creation but also updates, failure, rollback, and partial execution.

Chapter Summary

Infrastructure as Code expresses infrastructure through machine-readable, versioned, and automatable definitions. It evolved from manual administration and ad hoc scripts into a practice incorporating desired state, repeatability, review, testing, and governance. Declarative automation describes the intended result, whereas imperative automation describes the steps used to obtain it. Idempotency supports safe repetition; immutability reduces accumulated variation; reproducibility enables consistent environments; and drift management keeps actual infrastructure aligned with declared intent.

Terraform is primarily suited to provisioning and managing infrastructure resources and their relationships, while Ansible is primarily suited to configuring systems and applications. They may be combined when ownership boundaries are clear. IaC improves speed, consistency, auditability, recovery, and collaboration, but it introduces risks involving state, secrets, destructive changes, provider behavior, drift, and excessive automation. Effective IaC therefore requires technical design, version control, testing, security, governance, and professional judgment.

Key Terms

Infrastructure as Code: Managing infrastructure through machine-readable definitions and automated processes.

Provisioning: Creating, modifying, or deleting infrastructure resources.

Configuration management: Maintaining required software, settings, and services on systems.

Orchestration: Coordinating multiple dependent automation activities.

Desired state: The intended condition of an infrastructure resource or system.

Actual state: The condition that currently exists in the environment.

Drift: A difference between declared desired state and actual state.

Idempotency: The property that repeated application produces the same intended result.

Immutability: Replacing an artifact or instance instead of modifying it in place.

Reproducibility: The ability to recreate an equivalent environment from controlled definitions and dependencies.

Declarative automation: Automation that specifies the desired result rather than every action.

Imperative automation: Automation that specifies the procedural steps to execute.

State: Information used by an automation tool to track managed resources and prior conditions.

Terraform: A declarative infrastructure-provisioning and resource-management tool.

Ansible: An automation and configuration-management tool commonly using playbooks and modules.

Module: A reusable unit of infrastructure or configuration logic.

Provider: An integration that enables a tool to communicate with a platform or service API.

Policy-as-code: Expressing governance, security, or compliance rules in machine-evaluable form.

Configuration drift: Unplanned divergence caused by manual or external changes.

GitOps: An operating approach in which version-controlled declarations drive automated environment changes.

Review Questions

Short Answer Questions

1. Define Infrastructure as Code and state two characteristics that distinguish it from manual provisioning.

2. What is the difference between desired state and actual state?

3. Explain idempotency using an example of a configuration task.

4. Distinguish infrastructure provisioning from configuration management.

5. Why must Terraform state be protected in a team environment?

Long Answer Questions

1. Explain the evolution from manual provisioning to modern IaC, identifying the weaknesses addressed at each stage.

2. Compare declarative and imperative automation, including situations in which each may be appropriate.

3. Discuss how immutability and reproducibility improve infrastructure reliability, and identify their limitations.

4. Compare Terraform and Ansible in terms of purpose, operating model, and typical professional use.

5. Analyze the governance, security, and operational controls required for a mature IaC workflow.

Analytical / Application Questions

1. An organization uses Terraform to create virtual networks and Ansible to configure servers, but a shell script also changes firewall rules. Analyze the risks and propose an ownership model.

2. A development environment is manually changed to solve an urgent problem and now differs from its Terraform configuration. Explain the possible responses and recommend a safe reconciliation process.

3. A company wants to replace database servers automatically whenever configuration changes. Evaluate this proposal using the principles of immutability, persistent state, backup, and recoverability.

22. Multiple Choice Questions with Answers

Question 1

Which statement best describes Infrastructure as Code?

A. Using only shell scripts to start application processes

B. Managing infrastructure through versioned, machine-readable definitions and automation

C. Allowing administrators to configure servers manually through a console

D. Replacing all infrastructure with containers

Correct Answer: B. Managing infrastructure through versioned, machine-readable definitions and automation

Question 2

An Ansible task checks whether a package is installed and changes the system only when it is absent. Which principle does this illustrate?

A. Immutability

B. Drift

C. Idempotency

D. Manual provisioning

Correct Answer: C. Idempotency

Question 3

Which activity is most directly associated with Terraform’s primary role?

A. Creating a virtual network and managed database

B. Editing a developer’s local text editor settings

C. Writing application business logic

D. Monitoring user satisfaction surveys

Correct Answer: A. Creating a virtual network and managed database

Question 4

What is infrastructure drift?

A. A planned replacement of a server image

B. A difference between declared configuration and actual infrastructure

C. A dependency graph created before deployment

D. A method of encrypting state files

Correct Answer: B. A difference between declared configuration and actual infrastructure

Question 5

Which statement correctly contrasts declarative and imperative automation?

A. Declarative automation specifies the desired result, while imperative automation specifies steps

B. Declarative automation cannot use conditions, while imperative automation always uses them

C. Imperative automation is always idempotent, while declarative automation is not

D. There is no practical distinction between the two approaches

Correct Answer: A. Declarative automation specifies the desired result, while imperative automation specifies steps

Question 6

A team uses Terraform to provision cloud servers and Ansible to install packages and configure services on them. Why is this division generally sensible?

A. Terraform cannot communicate with cloud APIs

B. Ansible is designed only for database backups

C. Terraform commonly manages infrastructure resources, while Ansible commonly configures existing systems

D. The two tools must always manage every resource simultaneously

Correct Answer: C. Terraform commonly manages infrastructure resources, while Ansible commonly configures existing systems

Question 7

Which situation most strongly supports an immutable infrastructure approach?

A. A stateless application tier can be rebuilt from a versioned image

B. A database contains unreplicated production data

C. An administrator needs to inspect a running process once

D. A temporary command must be executed on one host

Correct Answer: A. A stateless application tier can be rebuilt from a versioned image

Question 8

Why should infrastructure changes undergo code review?

A. To ensure that every cloud provider uses identical syntax

B. To identify security, cost, dependency, and destructive-change implications before execution

C. To eliminate the need for testing and monitoring

D. To prevent all emergency operational changes

Correct Answer: B. To identify security, cost, dependency, and destructive-change implications before execution

Question 9

A resource is managed by Terraform, a console user, and an independent script. What is the principal risk?

A. The resource will necessarily become immutable

B. The tools may conflict and produce unpredictable or recurrent drift

C. Terraform will automatically remove all external systems

D. Ansible will become the only authorized manager

Correct Answer: B. The tools may conflict and produce unpredictable or recurrent drift

Question 10

Which practice best supports reproducibility?

A. Relying on undocumented manual changes after deployment

B. Allowing every run to use unrecorded dependency versions

C. Versioning or constraining important providers, images, modules, and inputs

D. Deleting all environment-specific information

Correct Answer: C. Versioning or constraining important providers, images, modules, and inputs

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 1 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 2
IaC Architecture and Delivery Lifecycle

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

2. IaC Architecture and Delivery Lifecycle

Introduction and Context

Infrastructure as Code (IaC) is not merely a technique for replacing manual server configuration with scripts. It is an operating model for designing, reviewing, delivering, governing, operating, and eventually retiring infrastructure through controlled, repeatable, machine-readable processes. In a DevOps environment, IaC connects architectural intent with executable change. Terraform commonly expresses the desired state of cloud and platform resources, while Ansible is often used to configure operating systems, applications, and services. In practice, organizations frequently combine both approaches within a broader delivery system.

The value of IaC depends less on the ability to write a resource block or a playbook than on the quality of the lifecycle surrounding that code. Poorly designed infrastructure code can reproduce errors at high speed, expose sensitive information, create uncontrolled environments, and make retirement difficult. A mature IaC capability therefore begins with requirements and architecture, continues through source control, review, testing, and environment promotion, and remains active during operations, change management, and decommissioning.

This chapter presents IaC as a lifecycle and governance discipline. It examines architectural decisions, repository design, branching and promotion strategies, Terraform and Ansible delivery workflows, documentation, operational feedback, security, and retirement. The central argument is that infrastructure is a product with a lifecycle, not a collection of disposable scripts.

Learning Objectives

After studying this chapter, the learner should be able to:

1. Describe the stages of an IaC delivery lifecycle from requirements to retirement.

2. Distinguish architectural intent, desired state, actual state, and configuration behavior.

3. Design repositories, environments, modules, inventories, and promotion paths for maintainable IaC.

4. Explain the roles of source control, branching, peer review, testing, and policy checks.

5. Compare alternative environment and release strategies and select an appropriate approach.

6. Analyze operational, security, governance, and organizational risks in IaC delivery.

7. Apply lifecycle principles to a realistic Terraform and Ansible scenario.

IaC as an Architectural Discipline

Traditional infrastructure delivery often separates architecture from implementation. An architect produces a diagram, an operations team provisions resources, and a security team reviews the result after deployment. IaC makes this separation less defensible because the implementation itself becomes an executable expression of architecture. Network boundaries, identity permissions, storage settings, scaling rules, monitoring integrations, and configuration defaults are represented in code and can be examined before they affect a running environment.

Three perspectives are useful. Desired state describes what the organization intends to exist: for example, a private application subnet, a database with encrypted storage, and a workload identity with restricted permissions. Actual state describes what currently exists in the platform. Configuration behavior describes how tools create, modify, or configure resources to reduce the difference between desired and actual state. Terraform primarily manages declarative resource state and relationships. Ansible generally performs configuration tasks, although idempotent playbooks also express a desired condition such as “the package is installed” or “the service is enabled.”

An IaC architecture should make several boundaries explicit:

  • The boundary between reusable infrastructure components and environment-specific composition.
  • The boundary between provisioning and configuration management.
  • The boundary between application delivery and platform delivery.
  • The boundary between code, state, secrets, and generated artifacts.
  • The boundary between individual team autonomy and organizational governance.

A sound architecture is modular without becoming fragmented. A Terraform module might create a standard network or managed database, while a root configuration composes those modules for a particular product environment. Ansible roles may provide a standard web server configuration, while inventories and variables describe the hosts on which the role is applied. Excessive abstraction, however, can hide important behavior. A module that accepts dozens of poorly documented parameters may be less maintainable than a smaller, explicit component.

From Requirements to an Infrastructure Design

The lifecycle begins before code is written. Requirements should identify workload behavior, availability expectations, data sensitivity, performance characteristics, geographical constraints, recovery objectives, compliance obligations, cost limits, and ownership. Infrastructure requirements are frequently incomplete because stakeholders express goals in application language. “The service must remain available during a zone failure” must be translated into topology, redundancy, health checks, deployment strategy, and recovery procedures.

A requirements-to-design process can be organized into five analytical activities.

First, establish service boundaries. Determine which components are owned by the platform team, product team, security function, or a managed provider. This prevents IaC repositories from becoming unclear mixtures of application, platform, and organizational responsibilities.

Second, identify non-functional requirements. Availability, latency, recovery time objective, recovery point objective, auditability, cost, and data residency influence architecture more strongly than the choice of syntax. A low-risk development environment should not automatically receive the same resilience and capacity as production, but its differences should be intentional and documented.

Third, model dependencies. A workload may depend on identity services, network routes, secrets, container registries, databases, DNS, certificates, observability systems, and external APIs. Dependency analysis helps determine provisioning order and ownership. It also exposes circular designs in which two components cannot be created independently.

Fourth, define invariants and policies. An invariant is a condition that must remain true, such as encryption being enabled, public access being prohibited for a database, or mandatory tags identifying an owner and cost centre. Invariants can be documented, tested, and enforced through policy-as-code.

Fifth, select lifecycle and failure assumptions. The design should state whether resources are persistent, replaceable, stateful, ephemeral, or shared. It should also identify what happens when a deployment fails, a region becomes unavailable, credentials expire, or a resource must be restored.

The output is not simply a diagram. It may include an architecture decision record, a dependency model, naming and tagging rules, data classification, environment definitions, recovery requirements, and a map of responsibilities. These artefacts provide the context against which infrastructure code can be reviewed.

The IaC Delivery Lifecycle

The lifecycle is iterative rather than strictly linear. Operational findings can change requirements, and retirement planning can influence the initial design. Nevertheless, the following stages provide a practical control model.

1. Discover and Specify

Stakeholders define the service, its constraints, ownership, environments, interfaces, and success criteria. The team determines whether a requirement is best implemented as a platform capability, a reusable module, an application configuration, or a manual exception. Ambiguity at this stage becomes expensive later because code tends to conceal unresolved decisions behind defaults.

2. Architect and Decompose

The desired platform is decomposed into networks, identity, compute, storage, data services, observability, and supporting controls. Components are assigned boundaries and interfaces. The team decides which parts should be reusable modules, which values should be variables, and which settings must be fixed by organizational policy.

3. Implement in Source Control

Infrastructure code, tests, documentation, examples, policy definitions, and pipeline configuration are committed to a version-control repository. The repository becomes the authoritative record of proposed and accepted change. Secrets should not be committed; they should be supplied through a secret manager, protected pipeline variable, or short-lived identity mechanism.

Terraform practitioners typically format and validate code, initialize providers, create a plan, and apply an approved plan. Ansible practitioners commonly lint YAML and roles, validate inventories, run syntax checks, test idempotence, and execute playbooks against controlled targets. Both tools benefit from pinned versions and explicit dependency management.

4. Review and Verify

A pull request or merge request provides a formal point for peer review. Reviewers examine not only syntax but also blast radius, identity permissions, data protection, naming, cost, rollback behavior, observability, and compatibility with existing resources. Automated checks may include formatting, static analysis, security scanning, unit tests for modules, policy tests, and plan analysis.

Terraform plans should be treated as change proposals, not as harmless previews. A plan that replaces a database or removes a route requires a different level of scrutiny from one that adds a non-production tag. Ansible changes should be assessed for privilege escalation, ordering, idempotence, service interruption, and behavior when a task partially succeeds.

5. Promote Through Environments

Approved changes should move through environments using a defined promotion model. A typical sequence is development, integration or testing, staging, and production. Promotion does not mean blindly copying a development state into production. It means promoting the same reviewed version of a component while supplying environment-specific values through controlled configuration.

Environment isolation can be implemented through separate Terraform state files, workspaces, directories, accounts or subscriptions, and Ansible inventories. Each option has trade-offs. Separate accounts provide strong isolation but may increase administration. Workspaces can be convenient but may obscure differences when used carelessly. Separate root configurations are explicit and often easier to review, although they require more repository structure.

6. Deploy and Observe

Deployment is an operational event, not the end of the process. Pipelines should record who approved a change, which commit was deployed, which plan or playbook ran, what identity was used, and what result occurred. Monitoring should verify both infrastructure health and service behavior. Logs, metrics, traces, configuration drift alerts, and cost signals provide evidence about whether the implementation meets its requirements.

7. Operate, Change, and Reconcile

Infrastructure changes continuously because of security patches, scaling, provider changes, incidents, and new product requirements. Teams must decide how emergency changes are handled. Direct console changes may be necessary during an incident, but they create drift and should be followed by reconciliation: either the code is updated to represent the approved change or the manual change is deliberately removed.

Drift is not always an error. Some platforms update managed resources automatically, and some attributes are controlled externally. The important question is whether the difference is understood, authorized, and sustainable. Regular plan reviews, drift detection, and ownership of exceptions prevent the actual environment from becoming an undocumented second source of truth.

8. Retire and Verify Disposal

Retirement includes more than deleting compute resources. It involves dependencies, DNS records, identities, snapshots, backups, certificates, monitoring alerts, state entries, data retention, billing associations, and documentation. Destruction must be authorized and, for regulated or valuable data, supported by retention and disposal evidence. A deliberate retirement workflow reduces orphaned resources and prevents credentials or access paths from surviving the service they supported.

Repository, State, and Documentation Design

Repository design communicates how a team thinks about ownership and change. One repository may contain a complete platform for a product; another may separate reusable modules from environment compositions. There is no universal structure, but a useful design keeps interfaces visible and limits hidden coupling.

A conceptual Terraform repository might contain reusable modules, environment root configurations, policy tests, pipeline definitions, and architecture documentation. An Ansible repository might contain roles, inventories, group variables, host variables, playbooks, collections, and test material. Generated files, local caches, plans containing sensitive values, and credentials should be excluded or protected appropriately.

Terraform state deserves particular attention. State maps configuration resources to objects managed by Terraform and may contain sensitive information. It should normally be stored in a protected remote backend with encryption, access control, locking where supported, versioning, and recovery procedures. State is not merely a cache that can be copied casually. A corrupted, exposed, or concurrently modified state can cause service disruption and security compromise.

Ansible does not use a Terraform-style state file for general configuration reconciliation, so the team must define how inventory, variables, facts, and external systems represent reality. This difference matters: Terraform commonly plans a resource graph from stored state and provider observations, whereas Ansible generally evaluates tasks against target systems during execution. Combining the tools requires clear ownership. Terraform may create instances and attach identities; Ansible may configure the operating system and application. Neither should silently overwrite the other's responsibilities.

Documentation should be treated as part of the deliverable. At minimum, a component should state its purpose, inputs, outputs, supported versions, dependencies, security assumptions, examples, ownership, operational procedures, and destruction implications. Architecture decision records are useful when a choice has meaningful alternatives or long-term consequences. Documentation that is generated from code can improve consistency, but generated documentation does not replace explanations of intent.

Branching, Review, and Promotion Strategies

A branching model is a coordination mechanism, not a goal. Long-lived feature branches can isolate work but increase merge conflicts and delay integration. Short-lived branches with frequent pull requests encourage small changes and rapid feedback. A trunk-based model may suit mature teams with strong automated testing and feature controls; a release-branch model may be appropriate where infrastructure changes require formal release windows or extended validation.

Regardless of model, effective practice includes small changes, descriptive commits, protected production branches, mandatory reviews for high-risk changes, and traceability from a requirement or incident to the deployed revision. Reviews should involve the right expertise. A platform engineer may understand resource behavior, while a security engineer may identify excessive permissions and a service owner may understand availability consequences.

Promotion strategies include:

  • Direct sequential promotion: the same commit is applied progressively to increasingly important environments.
  • Rebuild promotion: each environment is created from the same reusable definition, with environment-specific inputs.
  • Blue-green infrastructure: a new environment is created alongside the old one and traffic is switched after validation.
  • Canary or progressive delivery: a change is exposed to a limited portion of traffic before wider adoption.

Blue-green and canary approaches can reduce deployment risk but may temporarily increase cost and require compatible data migration. Rebuilding an environment improves repeatability but may be unsuitable for stateful systems without carefully designed data handling. The appropriate approach depends on failure tolerance, resource cost, statefulness, and organizational control requirements.

Testing and Policy as Code

IaC testing operates at several levels. Formatting and syntax checks detect basic defects. Static analysis identifies insecure or suspicious patterns. Unit tests can verify module inputs and outputs. Integration tests provision real or representative resources and validate behavior. End-to-end tests assess whether a complete service works. Post-deployment checks confirm health, connectivity, identity behavior, and observability.

Idempotence is especially important for configuration management. Running an Ansible playbook twice should normally produce no unnecessary changes after the first successful run. Non-idempotent commands, uncontrolled shell scripts, time-dependent logic, and tasks that depend on undocumented host conditions reduce confidence.

Policy as code converts governance requirements into executable rules. Examples include prohibiting public storage, requiring encryption, restricting permitted regions, enforcing tags, or limiting privileged roles. Policy tools can inspect Terraform plans, configuration files, or cloud APIs. Policies should be proportionate: an unreviewable collection of exceptions encourages teams to bypass controls. A mature system records exceptions, assigns owners, defines expiry dates, and distinguishes blocking policies from advisory warnings.

A Detailed Illustrative Case: Promoting a Retail Ordering Platform

Background

A fictional retailer, Northstar Retail, operates an online ordering platform. Its initial infrastructure was created manually in a cloud console. Development, testing, and production differ in undocumented ways, and an audit has identified public network paths, inconsistent tags, and excessive administrator permissions. The organization decides to establish a Terraform and Ansible delivery lifecycle.

Situation and Problem

The platform consists of a web tier, application services, a managed database, object storage, monitoring, and a deployment runner. The database contains customer order data and must not be publicly reachable. The service owner requires high availability across two availability zones, while the finance team requires cost visibility by product and environment. The team must introduce IaC without interrupting production.

Analysis

The team first maps ownership and dependencies. Terraform will manage network components, identity roles, compute or container resources, managed database settings, storage, and monitoring integrations. Ansible will configure a small set of virtual machines used for specialized integration services. Application deployment remains under the application pipeline. Production and non-production use separate cloud accounts, reducing accidental cross-environment access.

The team identifies several risks. Importing existing resources into state may produce incorrect assumptions. A naïve apply could replace the database. Manual security fixes may be lost if they are not represented in code. A shared administrator credential would undermine auditability. Finally, development settings must not be copied into production simply because the code is shared.

Approach and Solution

Northstar creates a repository containing reusable network, database, identity, and monitoring modules, together with explicit root configurations for development, staging, and production. Each environment has a separate protected state backend. State access is granted to the deployment identity rather than to every developer.

The team imports existing resources gradually and reviews the resulting plans. Before any production apply, it runs validation, security scanning, policy checks, and integration tests in a temporary environment. A policy blocks public database access and requires encryption, ownership tags, and approved regions. A second policy warns when a proposed change exceeds a cost threshold; high-impact changes require architecture approval.

Ansible roles configure operating-system hardening, service users, logging agents, and the integration service. Roles are tested for idempotence, and inventories are separated by account and environment. Secrets are retrieved at runtime from a managed secret store. The pipeline uses short-lived credentials through workload identity rather than stored cloud keys.

Changes are developed in short-lived branches. A pull request displays the Terraform plan and test results. Two reviewers approve production changes: one for platform correctness and one for security or operational risk. The same commit is promoted from development to staging and then production, while environment-specific values remain outside reusable module logic.

Outcome

The retailer gains a traceable infrastructure history, consistent security controls, and repeatable environment construction. Drift detection identifies two manually changed firewall rules, which are reconciled after review. A later database parameter change is first tested in staging and then promoted during an approved maintenance window. The organization also discovers that retirement must include monitoring alerts and deployment identities, not only compute resources.

Learning Points

The case demonstrates that IaC success depends on lifecycle controls rather than syntax alone. Importing existing infrastructure requires caution; state must be protected; environment separation should be deliberate; policy checks should be automated; and promotion must preserve traceability. Most importantly, operational ownership continues after deployment.

Risks, Limitations, and Common Mistakes

IaC introduces substantial benefits but does not eliminate complexity. Common mistakes include treating plans as automatically safe, granting pipelines excessive privileges, storing secrets in repositories or state, using mutable shared environments, and relying on copy-and-paste rather than reusable components. Another error is excessive modularization: abstractions that hide provider behavior can make review and troubleshooting difficult.

State locking and concurrency controls are essential where multiple pipeline executions could modify the same resources. Provider upgrades, module changes, and platform API changes should be tested because apparently minor version changes can alter defaults or resource behavior. Destructive operations require explicit safeguards, backups where appropriate, and a clear approval path.

Rollback is more complicated than reverting a Git commit. A code revert may not restore data, external side effects, schema changes, certificates, or provider-managed behavior. Teams should distinguish infrastructure rollback from application rollback and design forward-fix procedures where reversal is unsafe.

Organizational risks are also significant. If teams own code but not the resulting service, defects may be passed between development and operations. If all infrastructure changes require a central team, delivery may become a bottleneck. Platform teams should provide reusable capabilities and guardrails while preserving appropriate product-team autonomy.

Security, Governance, and Professional Responsibility

IaC repositories are privileged assets. Access should follow least privilege, with separate identities for development, testing, and production. Pipeline credentials should be short-lived where possible, and approvals should be recorded. Sensitive values must be excluded from logs, plans, pull-request comments, and generated documentation. Encryption, secret rotation, dependency control, and vulnerability scanning should cover both infrastructure code and the systems it creates.

Governance should be risk-based. Production destruction, identity changes, public exposure, and regulated data movement deserve stronger controls than harmless documentation edits. Audit records should connect the request, reviewer, commit, plan, identity, execution result, and observed outcome. Separation of duties may be required in regulated contexts, but it should not become a purely ceremonial approval process.

Professional responsibility includes communicating uncertainty. Engineers should not describe an untested plan as safe, claim that code guarantees compliance without evidence, or conceal manual changes because they are inconvenient to reconcile. Infrastructure decisions affect availability, privacy, finances, and sometimes public safety. Technical competence therefore includes the duty to make assumptions and limitations visible.

Current Developments and Emerging Practice

IaC is increasingly integrated with platform engineering, internal developer platforms, and policy automation. Developers may request approved infrastructure through service templates while platform teams provide standardized modules and controls. This model can reduce cognitive load, but templates must expose meaningful choices and avoid creating opaque platforms.

Ephemeral environments are being used for pull-request validation and integration testing. They improve feedback but require automated cleanup, cost controls, data isolation, and careful treatment of external dependencies. Infrastructure drift detection is also becoming more continuous, with event-driven alerts and automated remediation for narrowly defined policies.

Artificial intelligence can assist with code suggestions, documentation, plan explanation, anomaly detection, and policy review. It should not receive uncontrolled access to production credentials or be trusted to approve destructive changes without human accountability. Generated infrastructure may contain insecure defaults, hidden permissions, or provider-specific errors. Human review, automated testing, and explicit authorization remain necessary.

Declarative infrastructure is also expanding beyond traditional virtual machines into managed services, Kubernetes resources, identity, observability, and software supply-chain controls. As the scope expands, teams must avoid assuming that every resource has identical lifecycle semantics. A stateless compute instance, a customer database, and an identity policy require different testing, backup, approval, and retirement procedures.

Best-Practice Synthesis

A mature IaC delivery system generally follows these principles:

1. Begin with explicit service requirements, ownership, dependencies, and failure assumptions.

2. Keep architecture, code, policy, tests, and operational documentation connected.

3. Separate reusable components from environment composition and protect state independently.

4. Use version control as the authoritative change record and keep production changes reviewable.

5. Promote tested revisions through isolated environments rather than relying on manual reconstruction.

6. Treat plans, playbooks, credentials, and state as operationally sensitive.

7. Automate validation, security checks, policy enforcement, drift detection, and cleanup.

8. Design for observability, recovery, reconciliation, and retirement from the beginning.

9. Use the least privilege necessary and maintain traceable, time-bounded exceptions.

10. Prefer small, reversible changes, while recognizing that data and identity changes may not be reversible.

Chapter Summary

IaC is a lifecycle discipline that transforms infrastructure architecture into versioned, testable, reviewable, and operationally governed change. The lifecycle begins with requirements and architectural decomposition, continues through implementation, source control, review, testing, promotion, deployment, observation, and reconciliation, and ends with controlled retirement. Terraform commonly manages declarative infrastructure state, while Ansible commonly configures systems through repeatable, idempotent tasks; their boundaries must be explicit when used together.

Repository structure, state protection, documentation, branching, environment isolation, policy as code, and promotion strategy determine whether IaC remains maintainable at organizational scale. Security and governance must address privileged pipelines, secrets, drift, auditability, and destructive actions. The Northstar Retail case illustrates how importing existing infrastructure, separating environments, testing plans, and enforcing policies can reduce operational risk. Emerging practices such as ephemeral environments, internal platforms, continuous drift detection, and AI assistance offer value only when accompanied by human accountability and strong controls.

Key Terms

Desired state: The condition that infrastructure code declares should exist.

Actual state: The condition currently observed in a platform or target system.

Configuration drift: An unintended or unexplained difference between desired and actual state.

Terraform state: Data mapping declared Terraform resources to real infrastructure objects.

Ansible role: A reusable structure containing tasks, variables, handlers, templates, and related configuration.

Module: A reusable, parameterized collection of infrastructure definitions.

Root configuration: The environment-level Terraform configuration that composes modules and providers.

Idempotence: The property that repeated execution produces no unnecessary change after the desired condition is reached.

Environment promotion: Moving a reviewed infrastructure revision through controlled environments.

Policy as code: Expressing governance or security rules in machine-evaluable form.

Pull request: A proposed source-control change submitted for review and automated verification.

Blast radius: The extent of systems, resources, or users potentially affected by a change.

Remote backend: A protected service used to store and coordinate Terraform state.

Infrastructure module interface: The documented inputs, outputs, assumptions, and behavior exposed by a reusable component.

Ephemeral environment: A temporary environment created for a limited purpose and automatically removed afterward.

Architecture decision record: A document capturing an important design decision, its rationale, and consequences.

Drift reconciliation: The process of investigating and resolving differences between code and deployed infrastructure.

Workload identity: A mechanism allowing an automated workload to obtain authorized access without long-lived static credentials.

Infrastructure retirement: The controlled removal of resources, access, data, dependencies, and operational records.

Review Questions

Short Answer Questions

1. Distinguish desired state from actual state in an IaC system.

2. Why should Terraform state be stored in a protected remote backend?

3. What is the purpose of a pull request in infrastructure delivery?

4. Explain idempotence in the context of Ansible.

5. Identify two activities that must be included when retiring infrastructure.

Long Answer Questions

1. Explain the complete IaC delivery lifecycle from requirements discovery to retirement.

2. Discuss how repository structure, environment isolation, and state management affect IaC maintainability.

3. Compare trunk-based development and release-branch approaches for infrastructure code.

4. Explain how policy as code, testing, and peer review work together to reduce infrastructure risk.

5. Discuss the security and governance responsibilities associated with Terraform and Ansible pipelines.

Analytical / Application Questions

1. A production plan proposes replacing a managed database after a module upgrade. What analysis, safeguards, and approvals should occur before deployment?

2. An organization has separate development and production environments, but engineers frequently make console changes in production. Design a drift-reconciliation and governance process.

3. A platform team wants to create ephemeral environments for every pull request. Evaluate the benefits, risks, cost controls, data requirements, and retirement mechanisms that should be considered.

22. Multiple Choice Questions with Answers

Question 1

Which statement best describes the relationship between desired state and actual state in IaC?

A. Desired state is what the platform currently contains, while actual state is what the code intends

B. Desired state is the intended condition, while actual state is the condition observed in the platform

C. Both terms describe the contents of a source-control repository

D. Actual state exists only before the first deployment

Correct Answer: B. Desired state is the intended condition, while actual state is the condition observed in the platform

Question 2

A team wants to ensure that a production database cannot be publicly reachable. Which approach most directly implements this requirement as an automated control?

A. Adding a note to the architecture diagram

B. Relying on an administrator to inspect the console after deployment

C. Creating a policy-as-code rule that evaluates the proposed infrastructure

D. Using a longer Git commit message

Correct Answer: C. Creating a policy-as-code rule that evaluates the proposed infrastructure

Question 3

Why is a Terraform plan treated as a change proposal rather than as proof that a deployment is safe?

A. A plan cannot show any resource changes

B. A plan may propose high-impact actions such as replacement or deletion that still require analysis

C. Terraform plans are unrelated to the configured resources

D. Plans are generated only after production deployment

Correct Answer: B. A plan may propose high-impact actions such as replacement or deletion that still require analysis

Question 4

Which repository practice most effectively supports controlled environment promotion?

A. Copying and editing production files manually for every release

B. Promoting a reviewed revision while supplying controlled environment-specific values

C. Allowing each environment to use unrelated code branches indefinitely

D. Storing credentials beside environment variables in the repository

Correct Answer: B. Promoting a reviewed revision while supplying controlled environment-specific values

Question 5

What is the principal purpose of protecting Terraform state?

A. To prevent developers from formatting Terraform code

B. To preserve mappings to managed resources and prevent unauthorized or concurrent modification

C. To make Ansible inventories unnecessary

D. To ensure all infrastructure is created in one cloud account

Correct Answer: B. To preserve mappings to managed resources and prevent unauthorized or concurrent modification

Question 6

An Ansible playbook changes a service every time it runs, even when the desired configuration is already present. Which property is missing?

A. Idempotence

B. Environment isolation

C. State locking

D. Blue-green deployment

Correct Answer: A. Idempotence

Question 7

Which action best addresses a manual production firewall change discovered by drift detection?

A. Ignore it because the platform is already running

B. Delete the entire environment immediately

C. Investigate and authorize the change, then reconcile code and actual infrastructure

D. Disable future drift detection

Correct Answer: C. Investigate and authorize the change, then reconcile code and actual infrastructure

Question 8

Why might separate cloud accounts or subscriptions be preferred for production and non-production?

A. They guarantee that infrastructure code contains no defects

B. They provide stronger isolation and reduce accidental cross-environment access

C. They eliminate the need for source control

D. They make state and secrets publicly accessible

Correct Answer: B. They provide stronger isolation and reduce accidental cross-environment access

Question 9

Which retirement activity is most likely to be overlooked if a team focuses only on deleting compute resources?

A. Removing obsolete monitoring alerts and deployment identities

B. Running a code formatter

C. Creating an additional development branch

D. Increasing application capacity

Correct Answer: A. Removing obsolete monitoring alerts and deployment identities

Question 10

What is the most appropriate use of AI assistance in IaC delivery?

A. Allowing an AI system with unrestricted credentials to approve destructive production changes

B. Using AI for suggestions or plan explanation while retaining testing, review, and human authorization

C. Replacing all architecture decisions with generated code

D. Committing AI-generated secrets to the repository for convenience

Correct Answer: B. Using AI for suggestions or plan explanation while retaining testing, review, and human authorization

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 2 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 3
Terraform Fundamentals and HCL

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

3. Terraform Fundamentals and HCL

Introduction and Context

Infrastructure as Code (IaC) treats infrastructure configuration as software: it is written in files, reviewed, versioned, tested, and executed through automation. Terraform is a widely used declarative IaC tool for provisioning and managing resources across cloud platforms, SaaS products, networks, and private infrastructure. Rather than describing every command required to create a system, a Terraform configuration expresses the desired end state. Terraform then compares that declaration with the known and observed state of the environment and proposes a set of actions to reconcile the two.

This chapter develops the practical and conceptual foundations required to work safely with Terraform. It introduces the Terraform command-line workflow, HashiCorp Configuration Language (HCL), providers, resources, data sources, variables, outputs, expressions, dependencies, state, execution plans, and lifecycle rules. The emphasis is not merely on syntax. Professional Terraform practice requires understanding how configuration, state, provider APIs, collaboration, and operational risk interact.

Terraform is commonly used alongside configuration-management tools such as Ansible. Terraform is generally strongest at provisioning and changing infrastructure objects—networks, instances, clusters, databases, and access policies—whereas Ansible is often used to configure operating systems and applications after those objects exist. The boundary is not absolute, but distinguishing provisioning from configuration helps prevent duplicated responsibilities and unclear ownership.

Learning Objectives

By the end of this chapter, the learner should be able to:

1. Explain Terraform’s declarative model, state mechanism, and provider architecture.

2. Use the principal Terraform CLI commands in a controlled workflow.

3. Construct HCL configurations containing providers, resources, data sources, variables, locals, and outputs.

4. Write expressions involving references, collections, conditionals, functions, and iteration.

5. Distinguish implicit and explicit dependencies and identify when each is appropriate.

6. Interpret Terraform plans, including additions, changes, destructions, and replacements.

7. Apply lifecycle rules and safeguards to reduce accidental outages.

8. Design a reviewable, secure, and maintainable Terraform workflow for professional environments.

Terraform’s Declarative Model

Terraform configuration describes desired infrastructure rather than prescribing a sequence of imperative commands. For example, a configuration can state that a virtual network must contain a subnet and that a compute instance must be attached to that subnet. Terraform determines the order in which provider API operations should occur.

This model depends on three related representations. The configuration is the intended design written by the practitioner. The state records Terraform’s understanding of objects it manages and their relevant attributes. The real infrastructure is the set of objects currently present in the provider or platform. During planning, Terraform evaluates the configuration, refreshes or reads information about managed objects, and calculates a proposed transition from the current state to the desired state.

The state file is therefore not simply a cache. It is an important mapping between configuration addresses and real-world objects. It may contain identifiers, dependency information, and sensitive values. State must be protected, backed up, and normally stored in a controlled remote backend when a team is involved. Editing state directly is an exceptional operation, not a normal configuration technique.

Terraform is idempotent in an operational sense: applying the same configuration, when the environment already matches it, should normally produce no changes. Idempotence is not guaranteed if external systems mutate resources, providers have inconsistent behavior, or configurations use unstable values. Consequently, practitioners must understand both Terraform’s desired state and the behavior of the provider APIs on which it depends.

The Terraform CLI Workflow

A normal workflow begins by creating a working directory containing one or more `.tf` files. Terraform treats all configuration files in a directory as one module; filenames are primarily a way to organize content for readers.

`terraform init` initializes the directory. It downloads required providers and modules, configures the backend, and creates or updates dependency lock information. Initialization should be repeated when provider requirements, module sources, or backend configuration changes. The dependency lock file should generally be committed so that a team uses reviewed provider versions rather than resolving different versions opportunistically.

`terraform fmt` formats Terraform files according to standard conventions. Formatting is not merely cosmetic: consistent layout makes review easier and reduces noise in version-control changes. `terraform validate` checks whether the configuration is syntactically valid and internally consistent. Validation does not prove that credentials are correct, APIs are available, or the proposed infrastructure is operationally suitable.

`terraform plan` evaluates a proposed change without applying it. A saved plan can be produced with `terraform plan -out=tfplan`; the exact saved plan can later be supplied to `terraform apply tfplan`. In controlled pipelines, this separation allows one identity or process to create a plan and another authorized process to approve and apply that exact proposal. Plans should be reviewed for unexpected replacements, deletions, public exposure, privilege changes, and cost implications.

`terraform apply` executes a plan. Without a saved plan, Terraform normally creates a new plan and requests confirmation in interactive use. In automation, approval is commonly represented by an approved pipeline rather than an unrestricted command such as `terraform apply -auto-approve`.

`terraform destroy` proposes removal of objects managed by the configuration. It is powerful and should be restricted in production environments. Other useful commands include `terraform show` for displaying a plan or state representation, `terraform output` for reading declared outputs, `terraform state list` for listing state addresses, and `terraform import` for associating existing infrastructure with a resource address. Import does not automatically create a complete configuration; configuration must still be written and reconciled.

HCL as a Configuration Language

HashiCorp Configuration Language is a declarative language designed to be readable by humans and processed by tools. A block has a type, optional labels, and a body of arguments or nested blocks. For example:

```hcl

resource "aws_s3_bucket" "logs" {

bucket = var.log_bucket_name

tags = {

Environment = var.environment

ManagedBy = "terraform"

}

}

```

Here, `resource` is the block type, `aws_s3_bucket` is the resource type, and `logs` is the local name. The address of the object is `aws_s3_bucket.logs`. The expression `var.log_bucket_name` references an input variable.

HCL supports strings, numbers, booleans, lists, sets, maps, tuples, and objects. Types matter because providers often distinguish between a single value and a collection, or between an ordered list and an unordered set. Explicit type constraints improve module interfaces and make errors easier to detect.

Comments may be written with `#`, `//`, or block-comment syntax. Interpolation is usually implicit in modern HCL: an expression can be assigned directly, as in `name = var.environment`. String templates remain useful when a value combines literal text and expressions, such as `name = "${var.project}-${var.environment}"`.

Providers, Resources, and Data Sources

A provider is a plugin that translates Terraform operations into API calls for a platform or service. Provider configuration commonly includes a region, endpoint, or authentication-related settings, although credentials should normally be supplied through environment variables, workload identity, or a secrets system rather than committed to source code.

A minimal provider requirement may appear as follows:

```hcl

terraform {

required_providers {

aws = {

source = "hashicorp/aws"

version = "~> 5.0"

}

}

}

provider "aws" {

region = var.region

}

```

The `required_providers` block identifies the provider source and acceptable version range. Pinning or constraining versions supports reproducibility, while regular upgrades are necessary for security fixes and new platform capabilities.

A resource represents an object Terraform creates, updates, or deletes. Its type and name form a unique address within a module. Resource attributes may be fixed values, variable references, or expressions. Providers can expose multiple instances through aliases, allowing one configuration to work across regions or accounts:

```hcl

provider "aws" {

alias = "secondary"

region = var.secondary_region

}

resource "aws_vpc" "replica" {

provider = aws.secondary

cidr_block = var.replica_cidr

}

```

A data source reads information that already exists or is calculated externally. It does not normally create the object it describes. For example, a data source can locate an existing image, subnet, account identity, or certificate. The distinction between a resource and a data source is operationally important: confusing them can lead to attempts to manage an object that should remain owned by another team.

```hcl

data "aws_caller_identity" "current" {}

output "account_id" {

value = data.aws_caller_identity.current.account_id

}

```

Variables, Locals, and Outputs

Input variables define a module’s interface. A variable can have a description, type, default, and sensitivity marker:

```hcl

variable "environment" {

type = string

description = "Deployment environment"

validation {

condition = contains(["dev", "stage", "prod"], var.environment)

error_message = "Environment must be dev, stage, or prod."

}

}

variable "instance_count" {

type = number

default = 2

sensitive = false

}

```

Values can be supplied through variable definition files, command-line options, environment variables, or workspace and pipeline mechanisms. Sensitive marking affects display in command output; it is not encryption. Secrets still require secure storage and access control, and sensitive values may remain in state if a resource needs them.

Local values give names to expressions used repeatedly within a module. They improve readability and reduce duplication but do not create independently managed objects:

```hcl

locals {

common_tags = {

Project = var.project

Environment = var.environment

}

}

```

Outputs publish selected values from a module. They are useful for passing information between modules, displaying endpoints, or integrating Terraform with deployment systems. Outputs should be designed carefully because they become part of the module’s public interface. An output containing a password or token should be marked sensitive and should generally be avoided unless there is a compelling integration requirement.

Expressions, Collection Processing, and Modules

Terraform expressions combine values and references. Common operators support arithmetic, comparison, logical operations, and conditional selection:

```hcl

instance_type = var.environment == "prod" ? "m6i.large" : "t3.micro"

```

Functions support transformations such as `merge`, `lookup`, `contains`, `length`, `join`, and `coalesce`. A conditional expression should have compatible result types on both branches. Complex expressions should be decomposed with locals when that improves reviewability.

The `for_each` and `count` meta-arguments create multiple resource instances. `count` is suitable when instances are essentially interchangeable and indexed numerically. `for_each` is usually safer when objects have stable logical keys:

```hcl

resource "aws_iam_user" "team" {

for_each = toset(var.team_members)

name = each.value

}

```

Changing a list used with `count` can shift indexes and cause unwanted replacements. A map used with `for_each` preserves identity by key, provided those keys are stable. The choice is therefore a modeling decision, not merely a syntactic preference.

A module is a reusable collection of Terraform configuration. A root module is the working directory where Terraform is run; child modules are called from it. Modules should expose a small, purposeful interface through variables and outputs. Reuse is valuable, but excessive abstraction can hide provider behavior and make debugging difficult. A module should represent a coherent capability rather than every possible variation of a platform resource.

Dependencies and Graph Evaluation

Terraform builds a dependency graph to determine ordering and parallelism. An implicit dependency occurs when one object references an attribute of another:

```hcl

resource "aws_subnet" "app" {

vpc_id = aws_vpc.main.id

cidr_block = var.app_cidr

}

```

The reference to `aws_vpc.main.id` tells Terraform that the subnet depends on the VPC. Terraform can therefore create the VPC first and destroy the subnet before the VPC.

An explicit dependency uses `depends_on` when the relationship is real but not represented by a value reference:

```hcl

resource "aws_iam_role_policy_attachment" "app" {

role = aws_iam_role.app.name

policy_arn = var.policy_arn

}

resource "aws_instance" "app" {

ami = data.aws_ami.application.id

instance_type = var.instance_type

depends_on = [aws_iam_role_policy_attachment.app]

}

```

Explicit dependencies should be used sparingly. Overuse reduces parallelism and can conceal poor modeling. A dependency should describe a genuine ordering requirement, not simply attempt to force Terraform to behave like a manually scripted sequence.

Reading Plans and Applying Changes Safely

A plan is both a technical proposal and a governance artifact. Terraform commonly marks actions with symbols such as `+` for creation, `~` for in-place update, `-` for destruction, and `-/+` for replacement. Replacement is more disruptive than an in-place update and deserves particular attention. A change may trigger replacement because an attribute is immutable, a resource has a `ForceNew` behavior in the provider schema, or its identity has changed.

Plan review should ask several questions. Is the affected environment correct? Are all changes intentional? Does any update expose a service publicly, broaden permissions, alter encryption, or remove data? Are there changes outside the expected application boundary? Does the plan contain a replacement of a stateful resource? Is the provider version appropriate? Are cost or quota implications understood?

Plans are not absolute guarantees. Between planning and applying, external changes may occur, credentials may expire, provider behavior may differ, or an API may reject a request. For this reason, plan and apply should occur close enough in time to remain meaningful, and production systems should use remote state locking, controlled identities, audit logs, and approval policies.

Lifecycle Rules

Lifecycle meta-arguments refine how Terraform handles a resource. `create_before_destroy = true` requests creation of a replacement before the old object is removed. This can reduce downtime but may fail when names must be unique, capacity is limited, or the platform cannot support simultaneous objects.

`prevent_destroy = true` causes Terraform to reject a plan that would destroy the resource. It is a useful guard for critical databases or storage, but it is not a substitute for backups, access controls, or a recovery plan. It can also obstruct legitimate replacement and must therefore be managed deliberately.

`ignore_changes` tells Terraform to disregard changes to selected attributes. This is appropriate when an external system is explicitly authorized to manage those attributes, such as a platform adding operational metadata. It is dangerous when used to hide drift or silence unexpected changes. Ignoring a field transfers part of its ownership outside Terraform and should be documented.

Modern Terraform also supports preconditions and postconditions for expressing assumptions about resource values. These checks can stop an operation when a security, compliance, or operational invariant is not satisfied. Conditions should be meaningful and understandable; excessive checks can make configurations brittle.

Illustrative Case Study: A Multi-Environment Service Platform

Background

A fictional organization, Northstar Analytics, operates a web service in development, staging, and production. Initially, engineers created virtual networks, security groups, and compute resources manually through a cloud console. The environments drifted: production used stronger network controls, staging contained obsolete rules, and no reliable record explained which team owned particular resources.

Situation and Problem

The platform team needed reproducible environments without allowing every developer to modify production. It also needed to preserve an existing production database while introducing Terraform gradually. A careless first attempt could have treated all existing infrastructure as new and proposed destructive changes.

Analysis

The team separated ownership boundaries. Terraform would manage network foundations, security groups, and compute services. A database team would continue managing the production database, which would be referenced through a data source or carefully imported only if ownership later changed. The team selected stable keys for environment-specific objects and established a remote backend with locking and restricted access.

Approach and Solution

The root module accepted variables for environment, region, CIDR ranges, and instance sizing. Common tags were defined in locals. Network resources used implicit references so Terraform could construct the dependency graph. Application instances used `for_each` with stable service names rather than `count` indexes. Production storage resources received `prevent_destroy = true`, while replacements of stateless compute resources used `create_before_destroy` where the provider and naming constraints allowed it.

The team first ran `fmt`, `validate`, and `plan` in a non-production account. Existing production resources were inventoried and imported only where Terraform would become the authoritative owner. Each import was followed by configuration refinement and a plan expected to show no unintended changes. Pull requests displayed plans for peer review. A protected pipeline applied approved plans using a narrowly scoped identity.

Outcome

Development and staging became reproducible, and the team could identify proposed changes before execution. Production remained protected by ownership boundaries, state locking, approval controls, and lifecycle safeguards. The organization did not eliminate all operational risk, but it converted undocumented manual procedures into a reviewable delivery process.

Learning Points

The central lesson is that Terraform adoption is a design and governance activity as well as a coding activity. Stable resource identity, explicit ownership, carefully protected state, and plan review matter as much as valid HCL. Import is a migration technique, not a replacement for understanding the infrastructure being adopted.

Challenges, Risks, and Professional Considerations

State is a major security and reliability concern. It may contain credentials, private keys, connection strings, or other sensitive attributes even when configuration marks outputs as sensitive. Remote state should use encryption at rest, access control, versioning, auditability, and locking where supported. Backend credentials should not be embedded in source code.

Drift occurs when infrastructure changes outside Terraform. Some drift is legitimate, such as an approved autoscaling action; other drift indicates an unauthorized or undocumented change. Teams should decide whether a system is Terraform-authoritative, externally authoritative, or shared with clearly defined fields. `ignore_changes` should not become a general drift-hiding mechanism.

Provider and module supply chains require attention. Use trusted sources, constrain versions, review upgrades, and maintain the lock file. Provider credentials should follow least privilege. A plan that can modify identity policies or network boundaries should receive stronger review than a change to a development tag.

Common mistakes include committing secrets, using one state file for unrelated environments, applying from an unreviewed laptop, relying on `-auto-approve`, using unstable list indexes for long-lived objects, overusing `depends_on`, and assuming `sensitive = true` encrypts data. Another mistake is treating a successful apply as evidence that the architecture is secure or compliant; Terraform confirms execution, not organizational suitability.

Terraform also raises governance questions. Who may approve production changes? Who owns state backups? How are emergency changes recorded? How are regulated data locations, encryption requirements, retention periods, and segregation of duties enforced? Policy-as-code tools and CI checks can reject public storage, unrestricted ingress, or unapproved regions before apply, but policies must be maintained and tested like software.

Current Developments and Emerging Practice

Contemporary Terraform practice increasingly emphasizes remote execution, policy checks, drift detection, reusable modules, and automated plan review. Infrastructure teams are adopting ephemeral environments for testing, while attempting to control cloud cost and prevent the accumulation of abandoned resources. Provider ecosystems continue to expand, and provider upgrades require compatibility testing rather than blind adoption.

Declarative infrastructure is also being integrated with software supply-chain controls. Module registries, signed artifacts, dependency scanning, static analysis, and identity federation reduce the risks associated with downloading and executing infrastructure plugins. AI-assisted code generation may accelerate the creation of HCL, but generated configurations still require provider-specific validation, security review, cost analysis, and human accountability. An AI system cannot determine organizational ownership or approve a destructive production plan on its own.

Best-Practice Workflow

A disciplined workflow can be summarized as follows:

1. Define ownership, environment boundaries, and backend strategy before writing resources.

2. Pin or constrain provider versions and commit the dependency lock file.

3. Format and validate every change.

4. Use variables for genuine configuration differences and locals for readable reuse.

5. Prefer implicit dependencies through meaningful references.

6. Use stable keys with `for_each` for independently identifiable objects.

7. Generate and review plans, paying special attention to replacements and destruction.

8. Apply through a controlled identity with state locking and approval procedures.

9. Protect state and treat outputs as potentially sensitive.

10. Test modules and policy rules, document exceptions, and review provider upgrades.

Chapter Summary

Terraform provides a declarative method for managing infrastructure through configuration, provider APIs, and state. HCL expresses providers, resources, data sources, variables, locals, outputs, and reusable modules. Terraform’s CLI workflow—initialize, format, validate, plan, review, and apply—supports repeatable and auditable changes. Expressions and iteration make configurations adaptable, while implicit dependencies allow Terraform to construct an execution graph. Lifecycle rules such as `create_before_destroy`, `prevent_destroy`, and `ignore_changes` must be applied with a clear understanding of ownership and operational impact. Safe Terraform practice depends on protected state, constrained credentials, version control, plan review, policy enforcement, and careful treatment of replacements, drift, and sensitive data.

Key Terms

Provider: A Terraform plugin that communicates with an external platform or service.

Resource: A managed infrastructure object that Terraform can create, update, or destroy.

Data source: A read-only configuration construct used to retrieve existing or computed information.

State: Terraform’s record of managed objects and their attributes and relationships.

Backend: The mechanism used to store state and, where supported, coordinate locking.

HCL: HashiCorp Configuration Language used to write Terraform configuration.

Root module: The working directory from which Terraform is executed.

Child module: A reusable module called by another Terraform module.

Input variable: A declared value used to parameterize a module.

Local value: A named expression evaluated within a module.

Output: A value exposed by a module for users or other modules.

Expression: A combination of literals, references, operators, and functions that produces a value.

Implicit dependency: A dependency inferred from a reference between configuration objects.

Explicit dependency: A dependency declared with `depends_on`.

Execution plan: Terraform’s proposed set of changes needed to reconcile configuration and infrastructure.

Drift: A difference between Terraform’s managed understanding and externally changed infrastructure.

Lifecycle rule: A meta-argument controlling resource replacement, destruction, or change handling.

`for_each`: A meta-argument that creates instances identified by stable keys.

Idempotence: The property that repeated application of unchanged desired state produces no additional changes.

Plan replacement: A destroy-and-create operation required when an object cannot be updated in place.

Review Questions

Short Answer Questions

1. What roles do configuration, state, and real infrastructure play in Terraform’s reconciliation model?

2. Distinguish a Terraform resource from a data source.

3. Why is a provider version constraint important?

4. What is the purpose of `terraform plan`?

5. Why should sensitive outputs and state be protected separately?

Long Answer Questions

1. Explain the Terraform CLI workflow from initialization through application, including the role of formatting, validation, and plan review.

2. Discuss how variables, locals, outputs, and modules contribute to reusable Terraform design.

3. Explain implicit and explicit dependencies, including the risks of excessive use of `depends_on`.

4. Compare `count` and `for_each` as mechanisms for managing multiple resource instances.

5. Analyze the purpose and limitations of `create_before_destroy`, `prevent_destroy`, and `ignore_changes`.

Analytical / Application Questions

1. A plan proposes to replace a production database after a variable key changes. How would you investigate the cause and what safeguards would you apply before proceeding?

2. A development team wants to store cloud credentials in a `.tfvars` file committed to its repository. Evaluate this design and propose a safer workflow.

3. An organization has manually created production resources and wishes to adopt Terraform without disruption. Design an adoption strategy using ownership boundaries, import, state protection, and plan review.

22. Multiple Choice Questions with Answers

Question 1

Which statement best describes Terraform state?

A. It is only a temporary cache used during formatting

B. It records Terraform’s mapping between configuration objects and managed infrastructure

C. It is a provider-specific source-code repository

D. It replaces the need for configuration files

Correct Answer: B. It records Terraform’s mapping between configuration objects and managed infrastructure

Question 2

What is the principal purpose of terraform plan?

A. To execute all proposed infrastructure changes immediately

B. To install operating-system packages on managed instances

C. To calculate and display proposed changes before application

D. To delete resources not found in the current directory

Correct Answer: C. To calculate and display proposed changes before application

Question 3

Which construct is most appropriate for reading the identifier of an existing cloud account without creating it?

A. A data source

B. A lifecycle block

C. A count expression

D. A local-only resource

Correct Answer: A. A data source

Question 4

Why is for_each often preferable to count for long-lived objects with meaningful identities?

A. It prevents Terraform from creating more than one object

B. It identifies instances using stable keys rather than shifting numeric indexes

C. It automatically encrypts all resource attributes

D. It removes the need for a provider

Correct Answer: B. It identifies instances using stable keys rather than shifting numeric indexes

Question 5

A subnet references aws_vpc.main.id in its vpc_id argument. What does this normally create?

A. An implicit dependency on the VPC

B. A backend lock for the subnet

C. A request to ignore VPC changes

D. A data source for the subnet

Correct Answer: A. An implicit dependency on the VPC

Question 6

What is the principal risk of using ignore_changes indiscriminately?

A. Terraform will always destroy the resource

B. It can conceal drift or changes that should have been reviewed

C. It forces every resource to use count

D. It prevents state from being stored

Correct Answer: B. It can conceal drift or changes that should have been reviewed

Question 7

A plan displays `-/+` for a resource. What does this most commonly indicate?

A. A formatting-only change

B. An in-place update with no interruption

C. A replacement involving destruction and creation

D. A provider initialization failure

Correct Answer: C. A replacement involving destruction and creation

Question 8

Which practice provides the strongest protection for collaborative production Terraform?

A. Applying from any laptop with auto-approval enabled

B. Committing credentials alongside configuration

C. Using protected remote state, locking, reviewed plans, and controlled identities

D. Keeping all environments in one unversioned local state file

Correct Answer: C. Using protected remote state, locking, reviewed plans, and controlled identities

Question 9

What does sensitive = true on a variable or output primarily do?

A. It guarantees that the value is encrypted everywhere

B. It prevents the provider from receiving the value

C. It reduces exposure in normal command output but does not replace secure state handling

D. It makes the value immutable

Correct Answer: C. It reduces exposure in normal command output but does not replace secure state handling

Question 10

A team wants a replacement resource created before the old resource is removed. Which lifecycle setting addresses this intention, subject to platform constraints?

A. prevent_destroy = true

B. create_before_destroy = true

C. ignore_changes = all

D. depends_on = false

Correct Answer: B. create_before_destroy = true

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 3 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 4
Terraform State and Resource Lifecycle

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

4. Terraform State and Resource Lifecycle

Introduction and Context

Terraform is often introduced as a declarative tool: engineers describe the desired infrastructure, and Terraform determines how to create or modify resources to reach that desired condition. This description is accurate but incomplete. Terraform does not operate only from configuration files. It also relies on a record of what it believes it has created and how those objects correspond to configuration. That record is the Terraform state.

State is therefore the operational memory of a Terraform system. It connects configuration, provider data, and real infrastructure. It enables Terraform to calculate a plan rather than recreate every object on each run. In an individual experiment, state may be a local file. In a production environment, however, it becomes a shared, security-sensitive, and governance-critical artifact. Decisions about where state is stored, how it is locked, who may access it, and how it is recovered directly affect reliability and organizational risk.

This chapter examines state management and the Terraform resource lifecycle as one integrated problem. It explains local and remote state, backends and locking, resource addressing, import and move operations, dependency graphs, drift, replacement risk, and recovery practices. The discussion assumes Terraform’s declarative model and places particular emphasis on collaborative DevOps environments in which infrastructure changes must be reviewable, repeatable, and safe.

Learning Objectives

By the end of this chapter, a student should be able to:

1. Explain the purpose, structure, and limitations of Terraform state.

2. Distinguish local state from remote backends and evaluate locking strategies.

3. Interpret resource addresses, instances, dependencies, and planned actions.

4. Import existing infrastructure and move resources without unintended recreation.

5. Diagnose drift, replacement risks, state corruption, and failed operations.

6. Design secure and collaborative state-management practices.

7. Apply state-recovery and lifecycle methods to realistic operational scenarios.

Terraform State as a Reconciliation Record

A Terraform configuration expresses desired infrastructure. The provider exposes an external system such as a cloud platform, DNS service, or Kubernetes cluster. State records Terraform’s understanding of the relationship between the configuration and objects managed through the provider. During planning, Terraform compares configuration with state and, where necessary, refreshes relevant information from the provider. It then proposes actions that should reconcile the known world with the desired world.

State commonly contains resource instances, provider-related information, attributes returned by APIs, dependencies, and identifiers used to locate real objects. It may also contain sensitive values. Although state is represented in a machine-readable format, it should not be treated as a harmless cache or ordinary build output. A password, certificate material, token, database connection string, or other secret can appear in state even when it is not visible in a plan or configuration file.

The state file is not the infrastructure itself. Deleting state does not delete cloud resources; it deletes Terraform’s record of them. Conversely, manually changing a state file does not reliably change the corresponding infrastructure. This distinction is central to safe operations. Configuration and state should normally be changed through Terraform commands and controlled code review rather than by editing the serialized file directly.

Terraform’s model can be represented as a reconciliation loop:

Configuration + current state + provider observations → plan → apply → updated state.

The loop is useful, but it is not omniscient. Provider APIs may be eventually consistent, attributes may be computed, external teams may change resources, and failed operations may leave partial results. Terraform state is an informed model of infrastructure, not an infallible representation of reality.

Local State, Remote State, and Backends

By default, Terraform can maintain state locally in a state file in the working directory. Local state is convenient for learning, prototypes, isolated experiments, and personal development. It avoids service configuration and makes initial experimentation simple. Its weaknesses become serious when more than one person or automation process operates on the same infrastructure.

A local file is easy to overwrite, copy into an insecure location, or use concurrently. It may be omitted from version control, producing an incomplete repository, or accidentally committed, exposing identifiers and secrets. Two engineers can each plan from different versions of state and then apply conflicting changes. Local state also provides limited auditability and recovery unless the team builds additional controls.

A remote backend stores state in a shared service or storage system. Examples include object storage with a locking mechanism, Terraform Cloud or Terraform Enterprise, and other supported backend services. Remote state allows a team and its CI/CD systems to use a common authoritative record. A suitable backend can provide encryption, versioning, access control, locking, audit trails, and recovery capabilities. These properties are not automatic; they depend on configuration and the capabilities of the selected platform.

A backend has two distinct responsibilities. First, it defines where state is stored and how Terraform communicates with it. Second, it may provide operational features such as locking, state version history, encryption, and controlled execution. Backend configuration can often be parameterized for different environments, but the distinction between backend configuration and ordinary provider configuration must be understood. Backend credentials should not be embedded casually in source code or exposed in command-line history.

State separation is an architectural decision. A team may use separate state files for development, testing, staging, and production, or for distinct infrastructure domains such as networking, identity, and application platforms. Excessive separation can create cross-state coordination complexity; insufficient separation can enlarge the blast radius of a mistake. The appropriate boundary usually follows ownership, access-control needs, lifecycle differences, and dependency relationships.

Locking and Collaborative Execution

State locking prevents multiple operations from modifying the same state simultaneously. A lock is acquired before an operation that may write state and released after completion. Without locking, two applies can read the same starting state, produce incompatible changes, and allow the later write to overwrite the earlier state update. The infrastructure may then exist in a condition that the recorded state no longer describes accurately.

Locking is not the same as approval. A lock coordinates technical concurrency; it does not determine whether a change is authorized. In mature teams, locking is combined with pull requests, policy checks, protected environments, identity-based access, and an auditable CI/CD workflow.

A lock can remain after a process crashes or a network failure interrupts communication. Force-unlocking is consequently a recovery operation, not a routine way to resolve impatience. Before removing a lock, an operator should establish that no Terraform process is still running, inspect job logs, identify the lock owner and operation, and document the decision. Force-unlocking while another apply is active can cause state corruption or conflicting infrastructure changes.

A sound collaborative workflow generally follows this sequence: change configuration in a branch; validate and plan in an isolated or controlled context; review the plan; acquire the remote lock during the approved apply; record outputs and state-version information; and release the lock only after completion. Production plans should be generated close enough to application that their assumptions remain valid, particularly when infrastructure changes frequently.

Resource Addressing and Identity

Terraform addresses identify objects in a configuration and in state. A simple resource address has the form `aws_instance.web`. A resource using `count` has indexed instances such as `aws_instance.web[0]`, while a resource using `for_each` has key-based instances such as `aws_instance.web["blue"]`. Module paths are prefixed when a resource belongs to a module, for example `module.network.aws_subnet.private["a"]`.

Addressing matters because Terraform tracks identity through these addresses. Changing an address can make an existing object appear absent and a new object appear necessary, even when the real infrastructure object has not changed. This is especially common when a resource is renamed, moved into a module, or changed from an unkeyed collection to a keyed collection.

`count` and `for_each` have different identity consequences. Count-based indexes can shift when list ordering changes. For example, removing the first item can cause later indexes to represent different logical objects. `for_each` keys are generally more stable when they reflect meaningful identities, such as availability-zone names or service identifiers. The choice is therefore not merely syntactic; it influences long-term lifecycle safety.

Terraform’s dependency graph is built from references and explicit dependency declarations. If a database subnet references a network resource, Terraform can infer that the network must exist first. `depends_on` is appropriate when a real dependency is not visible through an attribute reference, but unnecessary explicit dependencies can make the graph conservative and reduce parallelism. The graph determines ordering, not business approval, and it cannot automatically understand every external dependency.

Lifecycle Actions: Create, Update, Destroy, and Replace

Terraform plans typically describe actions as creation, in-place update, destruction, or replacement. A replacement occurs when a changed attribute cannot be modified by the provider in place, when an immutable property changes, or when lifecycle rules require recreation. Plans may show replacement symbols or a destroy-and-create sequence. Replacement is often more disruptive than an update because it can change identifiers, addresses, IPs, attached data, or service availability.

The `lifecycle` block provides controls such as `create_before_destroy`, `prevent_destroy`, and `ignore_changes`. `create_before_destroy` can reduce downtime when the provider and naming constraints permit two objects to coexist. It can also fail when a unique name, quota, or exclusive attachment prevents parallel existence. `prevent_destroy` is a safety guard for critical resources, but it can block legitimate migrations and should not replace backup and recovery planning. `ignore_changes` can accommodate an intentional external controller, yet excessive use hides drift and weakens Terraform’s authority over the resource.

Replacement risk must be assessed from the actual plan, provider behavior, data sensitivity, and dependency graph. A small configuration edit can cause a large operational effect if it forces replacement of a foundational network, encryption key, database, or identity object. Plan review should therefore ask not only “Does the plan match the ticket?” but also “Which downstream resources depend on this object, and what happens if its identity changes?”

Importing Existing Infrastructure

Import is used when infrastructure already exists but is not represented in Terraform state. A resource block describing the object is created, and an import operation associates the provider-side object with a Terraform address. Import does not automatically produce a complete, correct configuration. After importing, the configuration must be reconciled with the object’s actual attributes and provider defaults.

A reliable import process begins with inventory and ownership verification. The operator identifies the exact provider identifier, confirms that the object should become Terraform-managed, defines its intended address, and writes a resource block. After import, the operator runs a plan and examines differences. Attributes that are required or semantically important must be represented explicitly; computed values should not be copied blindly. The goal is a stable configuration that produces no unintended changes.

Import is particularly useful during modernization, merger of manually created environments, and adoption of infrastructure-as-code by an existing operations team. It is also a governance event: once imported, the team must decide whether Terraform is authoritative and how future manual changes will be handled.

Moving Resources Safely

A resource move changes its Terraform address while preserving its relationship with the real object. This is required when a resource is renamed, moved into a module, or reorganized within a configuration. A declarative `moved` block records the old and new addresses, allowing Terraform to interpret the change as a move rather than a destroy-and-create operation.

For example:

```hcl

moved {

from = aws_instance.web

to = module.compute.aws_instance.web

}

```

The exact addresses must correspond to the prior and new configuration, including indexes or keys where applicable. A move should be tested with a plan that shows the intended address transition and no replacement. Teams should preserve move declarations long enough to support all relevant state transitions and remove them only under a deliberate state-history policy. Direct state manipulation commands can also move addresses, but declarative move statements are usually easier to review and reproduce.

Moving is not importing. Import associates an untracked external object with an address; moving changes the address of an object that Terraform already tracks. Confusing these operations can produce duplicate management, accidental recreation, or an orphaned state entry.

Drift and Reconciliation

Drift is a difference between Terraform’s desired configuration or recorded assumptions and the actual external infrastructure. It may result from a console change, an emergency command, an external controller, provider behavior, or an incomplete previous operation. A refresh-aware plan can reveal many forms of drift, but not every difference is detectable or meaningful. Some provider attributes are computed, normalized, eventually consistent, or unavailable through the API.

The correct response to drift depends on intent. If the external change is unauthorized, applying Terraform may restore the declared configuration. If it is legitimate, the team should update configuration and state through a reviewed change. If another system is intentionally authoritative for an attribute, a carefully scoped `ignore_changes` rule may be appropriate. Silently ignoring broad classes of drift creates a false sense of control.

Drift detection is both technical and organizational. Regular plans, cloud audit logs, policy controls, and restricted console access complement Terraform. A team should define who may make emergency changes, how those changes are recorded, and how temporary divergence is reconciled.

State Security and Governance

State security requires protection of confidentiality, integrity, and availability. Confidentiality controls include encryption at rest and in transit, restrictive identity policies, private storage endpoints where appropriate, and avoidance of broad read access. Integrity controls include versioning, access logs, controlled write permissions, immutable or protected backups, and separation of duties. Availability controls include replicated storage, tested recovery procedures, and documented backend dependencies.

State should be treated according to the most sensitive value it may contain. Masking a value in command output does not necessarily remove it from state. State should not be committed to a public repository, copied into tickets, pasted into chat, or exposed through diagnostic archives without review. Backend credentials, cloud credentials, and CI secrets should be supplied through secure identity mechanisms rather than hard-coded configuration.

Organizations should establish ownership for each state location, define retention and deletion policies, restrict production applies, and record state-changing operations. Regulatory requirements may affect data residency, retention, encryption-key management, and access review. The technical ability to read state should be granted only to roles that need it, because state access can be equivalent to access to sensitive infrastructure information.

State Recovery and Failure Handling

Recovery begins with diagnosis. Operators should determine whether the problem is a stale lock, a failed provider operation, a missing object, an incorrect address, a corrupted state version, or ordinary drift. They should preserve logs, identify the last known good state version, and avoid repeated applies that may compound uncertainty.

Remote backends with versioning make recovery substantially safer. A prior state version can be selected for restoration according to backend capabilities and organizational procedure. Restoration must be followed by a plan and careful comparison with real infrastructure; rolling state backward does not automatically roll infrastructure backward. If the state says an object exists when it does not, a refresh or targeted corrective operation may be required. If an object exists but is absent from state, importing it may restore management.

State surgery should be exceptional. Commands that remove or alter state entries can be useful when an object has been intentionally transferred to another state or when an address is irreparably wrong, but they do not change provider-side infrastructure. Every such operation should be reviewed, backed up, logged, and followed by a plan. In a severe incident, the safest approach may involve temporarily stopping automation, making the backend read-only if possible, reconstructing ownership, and involving platform and security specialists.

An Illustrative Case: Migrating a Shared Network State

Background

Northstar Digital, a fictional software company, managed its cloud network from a local state file maintained by one platform engineer. The network supported development and production workloads. Application teams had recently begun using CI/CD pipelines, and two pipelines were configured to apply changes against the same environment.

Situation and Problem

An engineer moved a subnet resource into a reusable module and changed a collection from `count` to `for_each`. A subsequent plan proposed destruction and recreation of several subnets. At nearly the same time, a second pipeline attempted an apply using an older copy of the local state. The team also discovered that a security-group rule had been modified manually during an incident.

Analysis

Three distinct problems were interacting. First, the collection change altered resource addresses; Terraform could not infer that the old indexed objects were the same logical objects as the new keyed instances. Second, local state provided no reliable concurrency control. Third, manual modification created drift, and the team had no documented decision about whether Terraform or the emergency operator was authoritative.

Approach and Solution

The team stopped both pipelines and backed up the local state files and configuration. It inventoried the actual subnets and identified the intended stable keys. Declarative `moved` blocks were added for address transitions where correspondence was certain. Objects whose correspondence could not be safely established were reviewed individually rather than forced through a bulk operation.

The team then configured a remote backend with encryption, restricted access, versioning, and state locking. A single controlled plan was generated and reviewed by the network owner. The manually changed security rule was compared with the approved emergency record; because the change was legitimate, configuration was updated and the rule was brought under normal review. The final plan showed address moves and the intended rule update, but no subnet replacement. Only after this evidence was accepted did the team apply the plan.

Outcome

The network remained available, the state became accessible to authorized automation, and concurrent applies were prevented. The organization also introduced separate state boundaries for shared networking and application infrastructure, mandatory plan review for production, and a recovery runbook covering stale locks and state-version restoration.

Learning Points

The case demonstrates that a lifecycle incident is rarely caused by one syntax error. Resource identity, state concurrency, collection design, drift policy, and organizational workflow interact. Stable addresses should be designed before large-scale adoption, remote state should be introduced before collaboration becomes critical, and every replacement plan should be assessed in terms of dependency and service impact.

Common Errors and Professional Trade-offs

A frequent mistake is storing state in a source-control repository because configuration is stored there. Version control is valuable for configuration, but it is not automatically an appropriate state backend or secret-management system. Another mistake is assuming that a successful plan proves safety. Plans depend on provider observations, credentials, timing, and the correctness of state; they require interpretation.

Teams also overuse `-target` to force a partial apply. Targeting can be useful during carefully controlled recovery, but routine use may leave dependent resources inconsistent and conceal the actual graph. Similarly, broad `ignore_changes` rules may silence plans while allowing unauthorized or accidental divergence. Manual editing of state is risky because the serialized structure contains relationships and metadata that are easy to damage.

There is a trade-off between centralized state and state fragmentation. A single state can simplify references but increases blast radius, lock contention, and access scope. Multiple states reduce those risks but require explicit interfaces, such as remote outputs, and careful coordination. The right design follows ownership and lifecycle boundaries rather than an arbitrary preference for either one state or many.

Emerging Practice in State Management

Modern Terraform operations increasingly treat state as a governed platform service rather than a file. Remote execution, policy-as-code, automated plan analysis, signed artifacts, and identity federation reduce dependence on individual workstations. Platform teams are also adopting ephemeral environments, which demand automated creation and destruction while preserving separation from production state.

Provider ecosystems and external controllers create new coordination challenges. Kubernetes operators, cloud-native controllers, and security automation may change objects independently of Terraform. Successful designs explicitly allocate authority: Terraform may own the resource, another controller may own selected fields, or a shared ownership protocol may be required. Automated drift detection and policy evaluation can identify conflicts earlier, but they cannot replace clear ownership.

Artificial intelligence can assist with plan summarization, anomaly detection, and identification of potentially destructive changes. It should not be granted unsupervised authority to approve state changes. Explanations generated by analytical tools must be checked against the actual plan, provider documentation, dependencies, and organizational policy.

Best-Practice Framework

A professional state-management design should include the following practices:

  • Use remote state for shared or production infrastructure, with encryption, versioning, access control, and locking.
  • Separate state according to ownership, sensitivity, lifecycle, and blast radius.
  • Prefer stable `for_each` keys for long-lived collections when meaningful identities exist.
  • Review plans for replacement, dependency impact, data loss, and address changes.
  • Use import for existing unmanaged objects and move operations for tracked address changes.
  • Treat force-unlock, state removal, and state restoration as documented recovery actions.
  • Maintain tested backups and verify that restoring state does not imply restoring infrastructure.
  • Define an explicit policy for console changes, emergency changes, and drift reconciliation.
  • Keep production applies within an auditable workflow using least-privilege identities.
  • Make resource ownership and lifecycle assumptions visible to application, security, and operations teams.

Chapter Summary

Terraform state is the record that connects declarative configuration with provider-managed infrastructure. It enables planning, but it is not the infrastructure itself and should not be treated as an ordinary file. Local state is useful for isolated work, whereas collaborative environments generally require a remote backend with encryption, access control, versioning, and locking. Resource addresses define identity; careless changes to indexes, keys, names, or module paths can cause replacement. Import associates existing infrastructure with state, while move operations preserve identity across address changes. Lifecycle rules influence creation, update, destruction, and replacement, but they require analysis of provider constraints and dependency graphs. Drift must be investigated as both a technical and governance issue. Secure state management includes least privilege, confidential handling, recovery procedures, controlled automation, and explicit ownership. The central professional principle is to make state, identity, concurrency, and lifecycle decisions deliberate before infrastructure changes are applied.

Key Terms

State: Terraform’s recorded representation of managed resource instances and their provider relationships.

Backend: The mechanism and location Terraform uses to store and access state.

Remote state: State stored in a shared service or remote storage system rather than only on a local workstation.

State locking: Coordination that prevents concurrent operations from writing the same state.

Resource address: The path Terraform uses to identify a resource or resource instance.

Resource instance: A particular object created from a resource, often represented through an index or key.

Drift: A difference between declared or recorded infrastructure and the actual external environment.

Import: The operation that associates an existing provider object with a Terraform address in state.

Move: A state-aware address transition that preserves the identity of an already managed object.

Dependency graph: Terraform’s representation of ordering relationships among resources and modules.

Replacement: Destruction and recreation of a resource instead of an in-place update.

In-place update: Modification of an existing resource without destroying its provider-side identity.

Lifecycle block: Terraform configuration controlling selected creation, update, and destruction behavior.

`create_before_destroy`: A lifecycle setting that requests creation of a replacement before destruction of the old object when possible.

`prevent_destroy`: A lifecycle setting that blocks planned destruction of a resource.

`ignore_changes`: A lifecycle setting that prevents selected attribute differences from producing changes.

State version: A stored historical revision of state maintained by a backend or recovery system.

Force-unlock: An administrative operation that removes a lock believed to be stale.

Blast radius: The extent of infrastructure and service impact resulting from a change or failure.

Remote execution: Running Terraform planning or applying within a controlled service rather than an individual workstation.

Review Questions

Short Answer Questions

1. Why is Terraform state more than a cache of provider data?

2. What problem does state locking address?

3. Distinguish importing a resource from moving a resource.

4. How can `for_each` provide more stable identity than `count`?

5. Why does restoring an earlier state version not automatically restore infrastructure?

Long Answer Questions

1. Explain the relationship among configuration, state, provider observations, planning, and application in Terraform’s reconciliation model.

2. Compare local and remote state with respect to collaboration, security, concurrency, auditability, and recovery.

3. Discuss how resource addresses and dependency graphs influence Terraform lifecycle behavior.

4. Explain the causes of drift and evaluate alternative responses to intentional and unintentional drift.

5. Design a state-management policy for a production organization, including backend security, locking, access control, recovery, and review.

Analytical / Application Questions

1. A team changes `aws_subnet.private[0]` to `module.network.aws_subnet.private["zone-a"]`, and the plan proposes destruction and recreation. What evidence and Terraform mechanisms should the team use to preserve the existing subnet?

2. Two CI pipelines begin applying changes to one local state file, and the second pipeline overwrites the first pipeline’s state update. Diagnose the failure and propose a technically and organizationally complete remedy.

3. A database resource shows replacement after a minor configuration change. How would you analyze the provider behavior, lifecycle settings, dependency graph, data-protection requirements, and approval process before allowing the apply?

22. Multiple Choice Questions with Answers

Question 1

What is the primary operational purpose of Terraform state?

A. To store provider installation binaries

B. To connect configuration with managed infrastructure and support planning

C. To replace cloud-provider authentication

D. To serve as a source-code repository

Correct Answer: B. To connect configuration with managed infrastructure and support planning

Question 2

Which capability most directly prevents two Terraform applies from writing the same remote state concurrently?

A. State locking

B. Resource import

C. The ignore_changes argument

D. A moved block

Correct Answer: A. State locking

Question 3

A resource changes from aws_instance.web to module.compute.aws_instance.web, but the real instance should remain unchanged. Which approach is most appropriate?

A. Delete the instance manually

B. Use a moved declaration for the address transition

C. Use prevent_destroy on an unrelated resource

D. Create a second instance and ignore the first

Correct Answer: B. Use a moved declaration for the address transition

Question 4

What is a key risk of changing a count-based collection when list ordering changes?

A. Terraform loses all provider credentials

B. Resource indexes may refer to different logical objects

C. Remote backends stop supporting encryption

D. The dependency graph is deleted permanently

Correct Answer: B. Resource indexes may refer to different logical objects

Question 5

What does importing an existing cloud resource accomplish?

A. It automatically makes the configuration complete and drift-free

B. It associates the existing object with a Terraform state address

C. It destroys the existing object and recreates it

D. It converts local state into remote state

Correct Answer: B. It associates the existing object with a Terraform state address

Question 6

Why is force-unlocking considered a recovery operation?

A. It always destroys resources protected by the lock

B. It may permit concurrent operations if another process is still running

C. It permanently disables all future locking

D. It changes every resource address

Correct Answer: B. It may permit concurrent operations if another process is still running

Question 7

A manually changed security-group rule differs from configuration. If the manual change was authorized and should remain, what is the most appropriate general response?

A. Ignore all future changes to the security group

B. Update configuration through review and reconcile Terraform management

C. Delete the state file immediately

D. Force-unlock the backend

Correct Answer: B. Update configuration through review and reconcile Terraform management

Question 8

Which statement about restoring an earlier state version is correct?

A. It automatically rolls the provider infrastructure back

B. It changes Terraform’s recorded model and must be followed by a careful plan

C. It removes the need for provider credentials

D. It always causes every resource to be replaced

Correct Answer: B. It changes Terraform’s recorded model and must be followed by a careful plan

Question 9

Which lifecycle setting can prevent Terraform from planning destruction of a critical resource, but may also block legitimate changes?

A. prevent_destroy

B. for_each

C. depends_on

D. import

Correct Answer: A. prevent_destroy

Question 10

Why should Terraform state receive strong access controls?

A. State can contain sensitive values and infrastructure identifiers

B. State is always larger than the configuration

C. State cannot be versioned remotely

D. State contains only public documentation

Correct Answer: A. State can contain sensitive values and infrastructure identifiers

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 4 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 5
Reusable Terraform Architecture

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

5. Reusable Terraform Architecture

Introduction and Context

Infrastructure as Code (IaC) becomes strategically valuable when infrastructure is treated not as a collection of manually created resources, but as a repeatable, reviewable, and evolvable software system. Terraform is particularly suited to this purpose because it describes desired infrastructure declaratively and uses a state model to determine the changes required to reach that desired condition. However, a configuration that works for one environment can become difficult to maintain when it is copied across teams, regions, accounts, subscriptions, or application portfolios.

Reusable Terraform architecture addresses this problem. It concerns the design of modules, repositories, providers, state boundaries, naming conventions, version constraints, and composition patterns that allow infrastructure code to scale without uncontrolled duplication. Reusability is not achieved merely by placing resources inside a module. A reusable architecture must expose an appropriate interface, isolate responsibilities, constrain dependencies, support change, and remain understandable to the teams that operate it.

This chapter examines Terraform architecture from both technical and organizational perspectives. It also evaluates workspaces, repository structures, and multi-account or multi-subscription deployment models. The central design question is not simply “How can code be reused?” but “What should be standardized, what should remain configurable, and where should infrastructure ownership boundaries be placed?”

Learning Objectives

By the end of this chapter, students should be able to:

1. Explain the role and anatomy of reusable Terraform modules.

2. Design module interfaces using variables, outputs, validation, and appropriate abstraction.

3. Apply naming conventions, version constraints, and provider configurations consistently.

4. Compare composition patterns and repository structures for different organizational contexts.

5. Evaluate Terraform workspaces and alternative state-isolation strategies.

6. Design deployment models for multiple cloud accounts, subscriptions, regions, and environments.

7. Identify architectural, security, governance, and operational risks in Terraform reuse.

8. Recommend an appropriate reusable architecture for a realistic organization.

From Resource Configuration to Infrastructure Architecture

A Terraform resource describes a provider object such as a virtual network, storage bucket, database, or identity policy. A configuration may begin with several directly declared resources, but direct resource management becomes problematic as the system grows. Similar resources are repeated with small variations, provider settings become scattered, and changes must be coordinated manually across directories.

A useful architecture separates three concerns. The first is resource implementation: the detailed Terraform resources required to create a capability. The second is composition: the combination of capabilities into an environment or platform. The third is deployment context: the account, subscription, region, credentials, state location, and environment in which the composition is applied.

For example, a network module may implement subnets, route tables, and security boundaries. An environment configuration may compose the network with a managed Kubernetes cluster and monitoring resources. A deployment context may select the production cloud account, region, provider aliases, and remote state backend. Keeping these concerns distinct makes the architecture easier to test, review, and evolve.

Terraform configurations are organized into modules. The root module is the directory from which Terraform commands are executed. A child module is called by the root module or by another module. A module should normally represent a coherent capability rather than an arbitrary grouping of resources. Examples include a virtual network, object-storage service, database service, or standardized application platform.

Module Design as Interface Design

A module is best understood as a software component with an interface. Its input variables are parameters, its resources are implementation details, and its outputs are the values that other modules may consume. This perspective discourages two common errors: exposing every internal detail and creating modules so generic that their behavior becomes difficult to understand.

A well-designed module generally contains:

  • resource and data declarations that implement one coherent capability;
  • input variables with descriptions, types, defaults where safe, and validation rules;
  • outputs that expose stable integration points;
  • local values for derived names, tags, or repeated expressions;
  • provider requirements and, where necessary, provider configuration expectations;
  • documentation and examples showing supported usage.

A variable should represent a meaningful decision. For instance, `environment`, `name`, `location`, `tags`, and `subnet_definitions` may be reasonable inputs for a network module. A variable for every minor resource argument may simply reproduce the provider schema and make the module harder to use. Conversely, hard-coding organization-specific values inside a supposedly reusable module reduces portability.

Type constraints communicate the contract of a module. A map of objects is often more expressive than several parallel lists because related attributes remain together. Validation can prevent invalid combinations before resources are planned. Sensitive values should be marked as sensitive, although sensitivity marking does not by itself encrypt or remove a value from state.

Outputs should expose values required for composition, such as a network identifier, subnet identifiers, endpoint address, or role ARN. Outputs should not expose every resource attribute merely because it is available. Excessive outputs create coupling: callers may begin depending on implementation details that the module owner later needs to change.

A module should also establish a clear ownership boundary. If a networking module creates a virtual network, it should normally own the network-level routing and subnet structure. An application module may consume subnet identifiers but should not silently modify the network’s foundational resources. Ownership boundaries reduce accidental interference and clarify which team reviews a change.

Abstraction and the Principle of Appropriate Reuse

There are three broad forms of Terraform reuse. A copy-and-adapt approach duplicates configuration and modifies it for each environment. It is initially simple but creates drift and repeated maintenance. A module abstraction packages a recurring capability behind an interface. It reduces duplication but introduces interface design and version-management responsibilities. A composition model combines several focused modules in a root configuration. It provides flexibility and usually scales better than a single monolithic module.

Abstraction should follow genuine repetition and a stable concept. If two resources look similar but have different lifecycle policies, security assumptions, and owners, forcing them into one highly parameterized module may produce a confusing abstraction. Reuse is valuable only when the common behavior is understood and intentionally governed.

Composition Patterns

The flat composition pattern calls several sibling modules from a root module. A root configuration might call network, identity, database, and application modules and connect them through outputs. This is transparent and makes dependencies visible. It is often appropriate for environment repositories.

The nested composition pattern allows a higher-level module to call lower-level modules. For example, a platform module may call a network module and a cluster module. This can provide a convenient product-like interface, but it may hide important decisions from the root configuration. Nested composition should therefore be used when the combined components represent a stable capability, not merely to shorten a root file.

A layered pattern separates foundational infrastructure from shared services and workload infrastructure. A foundation layer may contain identity, networking, and logging. A platform layer may contain container orchestration or database services. A workload layer may contain application-specific resources. Layers may be deployed separately when they have different owners, change rates, or state boundaries.

A factory pattern uses collections of objects to create repeated resources, often with `for_each`. It is useful for a set of similar services such as storage containers, application namespaces, or subscriptions. It should not be confused with a module that accepts unlimited options. A factory works best when each item follows the same lifecycle and policy.

Dependencies should preferably be expressed through data flow. If module B needs a subnet identifier produced by module A, passing that output to B creates an implicit dependency in the graph. `depends_on` is appropriate for exceptional ordering requirements that cannot be represented through an attribute reference, but broad use of it can reduce Terraform’s ability to plan independently and may conceal the real relationship between components.

Naming, Tags, and Address Stability

Naming conventions are an architectural control, not a cosmetic preference. A useful naming scheme is deterministic, readable, and compatible with provider restrictions. A common conceptual form is:

`<organization>-<workload>-<environment>-<region>-<component>`

The exact order should be standardized by the organization. Names should not depend on random values unless uniqueness is genuinely required. Names and tags should distinguish ownership, environment, cost center, data classification, and managed-by status where organizational policy requires them.

Terraform resource addresses are also important. Changing a resource’s address in configuration may cause Terraform to interpret it as a destroy-and-create operation even when the underlying object should remain. Stable keys in `for_each` are therefore preferable to positional indexes when objects have meaningful identities. When a deliberate refactoring changes addresses, Terraform’s `moved` blocks can record the relationship between old and new addresses and reduce unnecessary replacement.

Naming should be separated from identity where appropriate. A cloud resource’s display name may change without changing its logical purpose, while a stable Terraform key should remain constant. Teams should review naming changes for both operational readability and lifecycle impact.

Version Constraints and Provider Configuration

Terraform architecture depends on several version boundaries. The `required_version` constraint limits the Terraform CLI versions supported by a configuration. The `required_providers` block declares provider source addresses and acceptable provider versions. A lock file records selected provider versions and checksums so that different machines can install consistent dependencies.

Version constraints should express a tested compatibility range rather than an arbitrary preference. A lower bound communicates the minimum supported version; an upper bound can prevent untested major-version changes. The exact policy depends on the organization’s release process, but production systems should avoid unconstrained provider upgrades. Upgrades should be planned, tested, reviewed, and applied through the normal delivery process.

Provider configuration is normally placed in the root module because the root module knows the deployment context. Child modules should declare provider requirements but generally should not contain their own hard-coded provider configurations. This allows the same module to be used with different accounts, subscriptions, regions, or credentials.

Provider aliases support multi-region and multi-account designs. A root configuration may define a default provider and one or more aliases, then pass the appropriate provider explicitly to a child module. The child module must declare the relevant provider configuration aliases in its `terraform` block. Explicit provider passing is safer than relying on accidental inheritance, particularly when a module creates resources in more than one administrative boundary.

Provider credentials should be supplied through the cloud provider’s supported authentication mechanisms, environment variables, workload identity, or a secure CI/CD identity. Credentials should not be committed to Terraform files, variable files, or state. Provider configuration must also respect least privilege: a deployment identity should receive only the permissions required for its state and resource boundary.

State, Workspaces, and Repository Structures

Terraform state maps configuration resources to real infrastructure objects and stores attributes needed to calculate changes. State is therefore both operationally essential and security-sensitive. Remote backends provide shared storage, locking, access control, and, depending on the platform, versioning and recovery features.

A state boundary should reflect a meaningful ownership, lifecycle, and access boundary. Putting an entire enterprise into one state can make plans slow and changes risky. Creating a separate state for every small resource can create excessive coordination overhead. A practical boundary often corresponds to a layer, account, subscription, region, or independently operated service.

Workspaces provide multiple state instances for one configuration. They are useful when the same code and structure are applied to several closely related instances and when differences are limited to variable values. They are less suitable when environments require substantially different permissions, provider configurations, approval processes, or resource sets. A workspace name is not a strong security boundary; access to the configuration and backend must still be governed independently.

For highly separated production and development environments, separate root directories or repositories with separate backend configurations are often clearer than workspaces. The choice should be based on risk, governance, operational independence, and degree of configuration similarity rather than on convenience alone.

Common repository models include:

1. Monorepo: modules, environment configurations, policies, and documentation are maintained in one repository. This offers centralized visibility and coordinated changes but may require sophisticated ownership rules and CI filtering.

2. Module repository plus live-infrastructure repositories: reusable modules are versioned independently, while environment repositories consume released module versions. This supports clear contracts and independent release cycles.

3. Environment-oriented repositories: each account, subscription, or platform has a separate repository. This provides strong access and pipeline boundaries but can increase duplication unless modules are shared.

4. Terragrunt or orchestration-assisted structures: an additional tool may manage repetition, backend configuration, and dependency orchestration. Such tools can be useful, but they add another abstraction and should be governed as part of the platform.

A mature structure distinguishes reusable module source from live configuration. A module should normally be consumed using a pinned version or commit reference rather than an unreviewed moving branch. Live configurations should make environment-specific decisions visible, including backend settings, provider aliases, variable values, and dependency relationships.

Multi-Account and Multi-Subscription Deployment Models

Organizations often separate cloud resources across accounts, subscriptions, projects, or tenants for security, billing, regulatory, and operational reasons. Terraform can support these models, but the architecture must make administrative boundaries explicit.

In a separate-root model, each account or subscription has an independent root configuration and state. This is the strongest isolation model and is suitable where teams, credentials, or approval processes differ. Shared components can be consumed through remote state or published outputs, but remote-state access must be controlled because it can expose sensitive information.

In a provider-alias model, one root configuration defines several provider instances and passes them to modules. This is convenient for coordinated deployment, such as creating a network in one account and a related service in another. It creates a larger blast radius and requires careful credential management. The pattern should be reserved for genuinely coordinated changes.

A hub-and-spoke model places shared connectivity or security services in a hub account and workload resources in spoke accounts or subscriptions. Terraform configurations may be separated by administrative ownership, with carefully defined interfaces for network identifiers, routing, and identity. The architecture should avoid allowing a workload pipeline to acquire broad control over the hub.

Cross-account or cross-subscription data should be exchanged through explicit outputs, controlled remote state, data sources, or an approved service catalog. Teams should avoid hidden lookups that make a module dependent on undocumented names or manually created resources. The more administrative boundaries a deployment crosses, the more important it becomes to separate planning, approval, and execution responsibilities.

Illustrative Case: Northstar Retail’s Cloud Platform

Background

Northstar Retail operates an online store and a data platform. Its infrastructure has grown across two cloud subscriptions: a production subscription controlled by the platform team and a non-production subscription used by development teams. The organization has three regions, requires cost allocation by product team, and wants standardized networking, managed databases, and Kubernetes clusters.

Situation and Problem

The initial Terraform implementation copied a large configuration for each environment. Provider blocks were embedded inside modules, resource names differed between teams, and one shared state file contained networking, databases, and application resources. A provider upgrade caused an unexpected plan across unrelated services. Developers also had more access to production state than governance policy allowed.

Analysis

The main issue was not a lack of Terraform syntax knowledge. The design lacked boundaries. The state file did not match ownership boundaries, modules had weak interfaces, and deployment context was mixed with implementation. Copying configurations concealed differences rather than managing them deliberately.

Approach and Solution

Northstar introduced a module repository containing versioned network, database, and cluster modules. Each module declared provider requirements but did not hard-code credentials or deployment-specific provider blocks. Input variables were typed and validated, while outputs exposed only identifiers required by consumers.

Separate live-infrastructure directories were created for production and non-production. Within each subscription, foundation, platform, and workload states were separated. Production and non-production pipelines used distinct identities and backend access policies. Provider aliases were used only where a coordinated cross-subscription operation was necessary; otherwise, each root configuration used one explicit provider context.

A naming standard based on organization, product, environment, region, and component was adopted. Common tags were generated in root configurations and passed to modules. Module versions were pinned to tested releases, and provider lock files were committed. CI pipelines ran formatting, validation, security checks, plans, and policy checks before approval. Apply operations required an approved plan and environment-specific authorization.

Outcome

The new architecture reduced configuration duplication while making environment differences visible. A database module upgrade could be tested and released without changing every live configuration immediately. Production state access was restricted, and the blast radius of a faulty plan was reduced because foundation and workload changes were isolated.

Learning Points

The case demonstrates that reuse is inseparable from governance. Modules provide technical reuse, but repositories, state boundaries, provider identities, release policies, and ownership rules determine whether that reuse is safe. The most reusable design was not the most abstract design; it was the one with clear contracts and appropriately limited responsibility.

Risks, Limitations, and Common Mistakes

A frequent mistake is the “god module,” which creates networking, identity, databases, applications, monitoring, and policies in one abstraction. Such a module may appear convenient but produces a large blast radius, complex variables, slow plans, and unclear ownership.

Another mistake is excessive parameterization. Dozens of boolean variables often indicate that the module is trying to support incompatible designs. In such cases, two focused modules or a higher-level composition layer may be clearer.

Unpinned module sources and provider versions create non-reproducible builds. Conversely, pinning forever without an upgrade process leads to technical debt and security exposure. Version management must balance stability with deliberate maintenance.

Using workspaces as an access-control mechanism is unsafe. Likewise, storing secrets in variables does not make them safe if they are later persisted in state. State must be encrypted where supported, protected by strict access policies, backed up, and treated as sensitive operational data.

Implicit provider inheritance can deploy resources to the wrong region or account, particularly in multi-provider configurations. Explicit provider passing and plan review reduce this risk. Broad `depends_on` usage, manual resource creation, unstable `count` indexes, and unreviewed `-target` operations are additional sources of drift and lifecycle surprises.

Terraform also has limitations. It does not automatically resolve organizational ownership conflicts, design sound network architecture, or guarantee that a provider’s API behaves consistently. Some resources have eventual consistency, incomplete provider coverage, or provider-specific lifecycle behavior. Architectural judgment remains necessary.

Governance, Ethics, and Professional Responsibility

Infrastructure code can create identity systems, process personal data, expose networks, and incur substantial financial costs. Engineers therefore have responsibilities beyond successful execution. Code review should consider least privilege, data classification, public exposure, encryption, retention, auditability, and cost impact.

Policy as code can enforce organizational rules such as approved regions, mandatory tags, prohibited public storage, or restricted instance types. Policy checks should be treated as preventive controls, not substitutes for professional judgment. Exceptions must be documented, time-bounded where possible, and approved by accountable owners.

State and plan artifacts may reveal resource names, endpoints, configuration values, or sensitive metadata. Access should follow least privilege, retention should be defined, and logs should be protected. Separation of duties is particularly important for production: the person proposing a change need not be the sole person authorized to apply it.

Current Developments and Emerging Directions

Reusable Terraform architecture is increasingly connected with platform engineering. Platform teams publish approved modules as internal products, document their interfaces, provide examples, and measure adoption and reliability. Module registries and private catalogs can make the approved path easier than uncontrolled copying.

Policy automation, drift detection, continuous validation, and ephemeral environments are becoming common parts of Terraform delivery. These practices shift infrastructure management from occasional command execution toward a governed software delivery lifecycle.

Artificial intelligence can assist with configuration explanation, variable documentation, test generation, and plan summarization. It should not be permitted to apply infrastructure changes without human review and policy controls. Generated Terraform may contain insecure defaults, incorrect provider assumptions, or destructive lifecycle changes. Human accountability remains essential.

Best-Practice Design Checklist

A reusable Terraform architecture should normally satisfy the following conditions:

  • Modules represent coherent capabilities and have documented contracts.
  • Inputs are typed, validated, and limited to meaningful decisions.
  • Outputs expose stable integration values rather than internal implementation details.
  • Root modules own backend and deployment-specific provider configuration.
  • Provider and module versions are constrained, locked, tested, and upgraded deliberately.
  • Naming, tagging, and resource-address strategies are standardized.
  • State boundaries reflect ownership, lifecycle, access, and blast-radius considerations.
  • Workspaces are used only when their isolation and similarity assumptions are appropriate.
  • Multi-account and multi-subscription provider use is explicit and least-privileged.
  • Plans, policy checks, approvals, and applies are integrated into CI/CD.
  • State, credentials, and plan artifacts receive appropriate security protection.
  • Documentation explains both supported use and deliberate non-use cases.

Chapter Summary

Reusable Terraform architecture treats infrastructure as a software system composed of modules, root configurations, providers, state, and delivery controls. Modules should encapsulate coherent capabilities and expose carefully designed interfaces through typed variables and stable outputs. Composition patterns should make dependencies and ownership visible, while naming conventions and stable resource addresses reduce lifecycle surprises.

Provider requirements, lock files, and tested version constraints support reproducibility. Provider configurations belong primarily in root modules, with aliases used explicitly for multi-region or multi-account operations. Workspaces offer multiple state instances but are not a substitute for security or organizational isolation. Repository and state structures should reflect ownership, lifecycle, access, and deployment risk.

In multi-account or multi-subscription environments, separate roots provide stronger isolation, while provider aliases support coordinated operations at the cost of greater blast radius. Effective architectures combine technical reuse with governance, CI/CD, policy, least privilege, and careful state protection. The central professional skill is selecting the appropriate boundary: enough standardization to reduce duplication, but enough flexibility to preserve clarity and accountability.

Key Terms

Module: A reusable Terraform unit that packages resources behind an interface.

Root module: The configuration directory from which Terraform is executed.

Child module: A module called by a root module or another module.

Composition: Combining modules to create a larger infrastructure capability or environment.

Input variable: A typed value supplied to a module to control its behavior.

Output: A value exposed by a module for use by other configurations or modules.

Provider: Terraform’s plugin-based interface to a cloud or external platform API.

Provider alias: An additional named provider configuration used for a distinct context.

Version constraint: A rule specifying compatible Terraform or provider versions.

Lock file: A file recording selected provider versions and verification checksums.

Remote backend: A service that stores Terraform state outside the local machine.

State: Terraform’s record of the relationship between configuration and real resources.

Workspace: A named state instance associated with one Terraform configuration.

State boundary: The deliberate division of resources managed in one state file.

Resource address: Terraform’s logical path identifying a resource instance.

Drift: A difference between declared configuration, recorded state, and actual infrastructure.

Blast radius: The scope of potential impact from a change or failure.

Policy as code: Machine-evaluated rules that enforce infrastructure or governance requirements.

Provider inheritance: The mechanism by which provider configurations are made available to modules.

Remote state data: Controlled consumption of outputs from another Terraform state.

Review Questions

Short Answer Questions

1. What distinguishes a root module from a child module?

2. Why should Terraform module outputs be limited to stable integration values?

3. What is the purpose of a provider lock file?

4. Why are stable `for_each` keys important for resource lifecycle management?

5. Why should workspaces not be treated as security boundaries?

Long Answer Questions

1. Explain how input variables, outputs, validation, and provider requirements combine to form a reusable module interface.

2. Compare flat, nested, and layered composition patterns, including situations in which each is appropriate.

3. Evaluate the advantages and disadvantages of monorepos, module-plus-live repositories, and environment-oriented repositories.

4. Discuss how state boundaries should be selected in a large organization.

5. Explain the architectural and governance considerations involved in multi-account or multi-subscription Terraform deployments.

Analytical / Application Questions

1. An organization has one Terraform state file for all environments and allows developers to apply production changes. Propose a revised repository, state, and identity model, and justify each boundary.

2. A module has 35 boolean variables to support three different network designs. Analyze the design problem and recommend a more maintainable composition strategy.

3. A root configuration deploys resources to two regions using provider aliases, but the child module relies on implicit provider inheritance. Identify the risk and design a safer provider-passing approach.

22. Multiple Choice Questions with Answers

Question 1

Which characteristic most strongly indicates that a Terraform module is well designed?

A. It exposes every argument of every resource it creates

B. It represents a coherent capability with a documented and limited interface

C. It contains provider credentials so it can deploy independently

D. It combines all infrastructure required by an entire organization

Correct Answer: B. It represents a coherent capability with a documented and limited interface

Question 2

Which approach best preserves resource identity when a collection of objects may be reordered?

A. Use count with list indexes

B. Use random names for every instance

C. Use for_each with stable meaningful keys

D. Use depends_on for every resource

Correct Answer: C. Use for_each with stable meaningful keys

Question 3

Where should deployment-specific provider configuration normally be placed?

A. Inside every child module

B. In the root module

C. Only in the Terraform lock file

D. In application source code

Correct Answer: B. In the root module

Question 4

What is the primary purpose of Terraform’s dependency lock file?

A. To store cloud credentials

B. To define resource naming conventions

C. To record selected provider versions and checksums

D. To replace Terraform state

Correct Answer: C. To record selected provider versions and checksums

Question 5

Which situation is generally most appropriate for Terraform workspaces?

A. Strongly isolated production and development environments with different access policies

B. Closely related instances using the same configuration and similar governance

C. A replacement for backend access controls

D. A way to combine unrelated accounts in one state

Correct Answer: B. Closely related instances using the same configuration and similar governance

Question 6

A child module must create resources in a secondary region. Which design is safest?

A. Hard-code credentials for the secondary region in the module

B. Rely on whichever provider Terraform inherits automatically

C. Define the provider requirement and pass the aliased provider explicitly

D. Create a separate Terraform installation inside the module

Correct Answer: C. Define the provider requirement and pass the aliased provider explicitly

Question 7

Why are separate state files often used for foundation and workload infrastructure?

A. They eliminate the need for configuration files

B. They reduce blast radius and reflect different ownership or lifecycle boundaries

C. They guarantee that no resource can ever drift

D. They allow credentials to be committed safely

Correct Answer: B. They reduce blast radius and reflect different ownership or lifecycle boundaries

Question 8

Which is a major risk of a monolithic 'god module'?

A. It cannot contain any variables

B. It prevents Terraform from creating resources

C. It creates unclear ownership, complex interfaces, and a large blast radius

D. It always requires multiple cloud providers

Correct Answer: C. It creates unclear ownership, complex interfaces, and a large blast radius

Question 9

In a multi-subscription architecture, what is the main advantage of a separate-root model?

A. It makes all subscriptions share one state file

B. It provides stronger administrative, access, and lifecycle isolation

C. It removes the need for provider configuration

D. It automatically synchronizes all resources

Correct Answer: B. It provides stronger administrative, access, and lifecycle isolation

Question 10

Which practice best supports controlled Terraform module evolution?

A. Consume moving branches without testing

B. Pin tested module and provider versions and upgrade them deliberately

C. Copy every module into each environment

D. Avoid version constraints entirely

Correct Answer: B. Pin tested module and provider versions and upgrade them deliberately

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 5 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 6
Ansible Foundations and Inventory Management

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

6. Ansible Foundations and Inventory Management

Introduction and Context

Infrastructure as Code (IaC) treats infrastructure configuration as a form of executable, reviewable, and version-controlled code. Terraform is primarily concerned with provisioning and managing infrastructure resources, whereas Ansible is commonly used to configure operating systems, deploy applications, enforce desired state, and orchestrate operational procedures. In a DevOps environment, the two technologies are often complementary: Terraform may create virtual machines and networks, while Ansible prepares those machines for service.

Ansible is an agentless automation platform. A central control node connects to managed nodes, usually through SSH for Linux and WinRM or PSRP for Windows, and executes modules remotely. This architecture reduces the software that must be installed on target systems, although it places responsibility for connectivity, credentials, interpreter availability, and execution control on the automation environment.

The effectiveness of Ansible depends not only on writing playbooks. Practitioners must understand inventories, variable resolution, facts, modules, connection methods, execution models, idempotence, security, and precedence. Poorly designed inventory or variable structures can make an apparently simple automation project difficult to test, audit, and operate. This chapter develops these foundations and relates them to professional infrastructure management.

Learning Objectives

After studying this chapter, the learner should be able to:

1. Describe Ansible’s architecture, agentless model, and principal execution stages.

2. Configure a basic Ansible control node and validate connectivity to managed hosts.

3. Design static and dynamic inventories using groups, variables, and host patterns.

4. Explain facts, modules, ad hoc commands, playbooks, tasks, handlers, and idempotence.

5. Select appropriate connection methods for Linux, Windows, network, and local targets.

6. Apply Ansible variable precedence and troubleshoot unexpected values.

7. Evaluate security, governance, reliability, and maintainability risks in Ansible automation.

8. Develop an inventory and automation approach suitable for a multi-environment organization.

Ansible’s Operating Model

Ansible uses a control-node model. The control node contains the Ansible executable, configuration, inventories, credentials or credential references, roles, collections, and playbooks. Managed nodes are the systems on which actions occur. A control node may be a developer workstation, a dedicated automation server, a CI/CD runner, or an execution environment managed through an automation platform.

The usual execution sequence is as follows. Ansible first parses configuration and inventory data, resolves the hosts selected by a play, loads variables, establishes connections, gathers facts if requested, and then executes tasks. Each task normally invokes a module. The module performs an operation on the managed node and returns structured output, including whether the system changed. Ansible uses this result to determine whether later tasks, handlers, or failure controls should be triggered.

Unlike traditional management systems that require a persistent agent on every server, Ansible generally transfers a temporary module to the target, executes it, collects the result, and removes temporary files. For many Unix-like systems, the target needs SSH access and a suitable Python interpreter for most modules. Some modules and connection plugins have different requirements. Agentless does not mean dependency-free: network access, authentication, privilege escalation, interpreters, and compatible modules remain essential.

Control Nodes and Managed Nodes

A control node should be treated as a production automation component rather than merely a command-line workstation. It requires controlled software versions, protected credentials, reliable access to target systems, and a directory structure that supports review and testing. Organizations commonly place playbooks in a Git repository and execute them through a controlled pipeline rather than allowing arbitrary manual changes.

Managed nodes may include virtual machines, physical servers, containers, network devices, cloud instances, and application hosts. Their connection requirements differ. Linux systems often use SSH; Windows systems commonly use WinRM or PSRP; network devices may use network-specific connection plugins and do not necessarily provide a conventional shell; and local execution can be used for a control node or development environment.

Installing and Validating a Control Node

Ansible is generally installed through Python package management or an operating-system package manager. In professional settings, the installation method should be reproducible. A virtual environment, pinned dependency versions, and a documented supported version reduce the risk that an update changes module behavior or parsing rules.

A basic validation sequence might include:

```bash

python3 -m venv .venv

source .venv/bin/activate

python -m pip install --upgrade pip

python -m pip install ansible

ansible --version

```

The exact installation command depends on the chosen Ansible distribution and organizational standards. The important principle is that the executable and its dependencies should be identifiable and repeatable.

Ansible searches for configuration in defined locations, with the active configuration commonly selected through the `ANSIBLE_CONFIG` environment variable or a project-local `ansible.cfg`. A minimal configuration may contain:

```ini

[defaults]

inventory = ./inventory

host_key_checking = True

retry_files_enabled = False

interpreter_python = auto_silent

[ssh_connection]

pipelining = True

```

Configuration should not be copied without analysis. Host-key checking improves protection against impersonation, while pipelining can improve performance but may interact with privilege escalation policies. Security and operational decisions should be explicit rather than hidden in a shared file.

Connectivity can be tested with an ad hoc command:

```bash

ansible all -m ansible.builtin.ping

```

The `ping` module does not test the ICMP protocol. It verifies that Ansible can connect, execute the module, and obtain a valid response. A successful result indicates functional automation connectivity, not that an application is healthy.

Inventory as a Model of the Environment

An inventory identifies managed hosts and organizes them into groups. It is also a source of host and group variables. Inventory therefore serves two purposes: it describes where automation should run and supplies context about those targets.

A simple INI inventory is easy to read:

```ini

[web]

web01 ansible_host=192.0.2.21

web02 ansible_host=192.0.2.22

[database]

db01 ansible_host=192.0.2.31

[production:children]

web

database

[production:vars]

environment=production

```

The host name used in a play is the inventory alias, while `ansible_host` identifies the actual network address. This distinction is useful when DNS names, cloud instance identifiers, and operational names differ.

YAML inventory is more structured and can represent nested variables clearly:

```yaml

all:

children:

web:

hosts:

web01:

ansible_host: 192.0.2.21

web02:

ansible_host: 192.0.2.22

vars:

service_name: nginx

database:

hosts:

db01:

ansible_host: 192.0.2.31

vars:

environment: production

```

The special `all` group contains every host, while `ungrouped` contains hosts not assigned to another explicitly defined group. Groups can represent technical roles, environments, geographical locations, ownership, compliance boundaries, or combinations of these. However, excessive overlapping groups can make host selection and variable inheritance difficult to understand. Inventory design should reflect stable operational concepts rather than every possible classification.

Static and Dynamic Inventories

A static inventory is maintained as a file. It is suitable for environments whose membership changes slowly or for controlled laboratory systems. A dynamic inventory obtains hosts from an external source such as a cloud provider, virtualization platform, configuration database, or service catalog. It may be implemented by an inventory plugin or an executable inventory script, although modern practice favors supported inventory plugins.

Dynamic inventory reduces the risk of configuring obsolete hosts, but it introduces dependencies on APIs, permissions, tags, and data quality. A cloud inventory may group machines by region, account, environment, or resource tags. If tags are inconsistent, the resulting automation scope may be incorrect. Therefore, dynamic inventory is not automatically more accurate; it is only as reliable as the source data and filtering rules.

Inventory can be inspected with:

```bash

ansible-inventory --graph

ansible-inventory --list

ansible-inventory --host web01

```

These commands are valuable for troubleshooting group membership and variable sources before a playbook is executed.

Variables, Facts, and Precedence

Variables allow one playbook to operate across hosts that differ in names, ports, packages, paths, environments, or operational policies. They can be defined in inventory, group and host variable directories, playbooks, roles, extra variables, and other sources.

A typical project might use:

```text

project/

├── ansible.cfg

├── inventory/

│ ├── production/

│ │ ├── hosts.yml

│ │ ├── group_vars/

│ │ │ └── web.yml

│ │ └── host_vars/

│ │ └── web01.yml

│ └── staging/

├── playbooks/

│ └── site.yml

└── roles/

```

Group variables express common properties, while host variables should be reserved for genuine exceptions. Sensitive values should not be stored as unencrypted plain text. Ansible Vault can encrypt files or variables, but Vault is a mechanism for protecting stored data, not a replacement for access control, secret rotation, or a dedicated secrets manager.

Facts are information gathered from managed nodes, such as operating-system family, processor architecture, memory, network interfaces, mount points, and discovered addresses. Fact gathering is enabled by default in ordinary plays unless disabled. Facts are accessed through the `ansible_facts` namespace, for example:

```yaml

  • name: Display operating system family

ansible.builtin.debug:

var: ansible_facts.os_family

```

Facts support conditional logic and portable roles, but fact gathering consumes time and may expose information that should be handled carefully. Fact caching can improve performance for large environments, but stale cached facts can produce incorrect decisions. Cache duration and invalidation should therefore be managed deliberately.

Understanding Precedence

Ansible has a detailed variable precedence model. In broad terms, values supplied explicitly at execution time, particularly through `-e` or `--extra-vars`, override many other definitions. Role and task variables, block and play variables, host and group variables, and inventory defaults operate at different levels. The exact ordering depends on the variable source and context.

The practical rule is not to memorize an isolated list but to design for clarity:

  • Use inventory variables for target-specific data.
  • Use role defaults for values intended to be easily overridden.
  • Use role variables for internal role behavior that should be less easily overridden.
  • Use play or task variables for limited execution context.
  • Use extra variables for deliberate, auditable overrides rather than routine configuration.

Unexpected values should be investigated using `ansible-inventory`, carefully scoped debug tasks, and documentation of the intended source. Variable names should be specific enough to avoid collisions, especially in collections and reusable roles.

Modules, Tasks, Plays, and Playbooks

A module is a unit of Ansible functionality. Modules manage packages, files, services, users, cloud resources, databases, templates, and many other objects. A task invokes one module with arguments:

```yaml

  • name: Ensure web package is installed

ansible.builtin.package:

name: nginx

state: present

```

A play maps a set of tasks to selected hosts:

```yaml

  • name: Configure web servers

hosts: web

become: true

tasks:

  • name: Install web server

ansible.builtin.package:

name: nginx

state: present

```

A playbook is a YAML document containing one or more plays. It represents a repeatable operational procedure. Tasks should use fully qualified collection names such as `ansible.builtin.copy`, improving readability and reducing ambiguity.

Modules differ in quality and behavior. A module should be selected according to the desired state of a resource rather than merely the command that can produce an outcome. For example, the `service` or `systemd` module communicates the desired service state more clearly than a shell command such as `systemctl start nginx`.

Ad Hoc Commands

Ad hoc commands are useful for exploration, diagnostics, and small one-time actions:

```bash

ansible web -m ansible.builtin.command -a 'uname -a'

ansible web -m ansible.builtin.setup

ansible db -b -m ansible.builtin.service -a 'name=postgresql state=started'

```

They are not a substitute for playbooks in controlled production work. Commands executed ad hoc are harder to review, reproduce, test, and audit. A useful professional pattern is to use an ad hoc command to investigate a problem, then encode the durable correction in a playbook.

Templates, Handlers, and Loops

The `template` module renders a Jinja2 template using variables and facts. It is suitable for configuration files whose contents vary by environment or host. When a configuration change requires a service restart, the task should notify a handler:

```yaml

  • name: Deploy application configuration

ansible.builtin.template:

src: app.conf.j2

dest: /etc/example/app.conf

mode: '0644'

notify: Restart application

handlers:

  • name: Restart application

ansible.builtin.service:

name: example-app

state: restarted

```

Handlers run when notified and normally execute at the end of a play. This avoids restarting a service unnecessarily when the configuration has not changed. Loops allow a task to apply an operation to multiple items, but loop data should be structured clearly and should not conceal substantially different operations in one task.

Idempotence and Execution Strategy

Idempotence means that applying the same intended configuration repeatedly produces the same final state without unnecessary additional changes. In Ansible, idempotence is expressed through module parameters such as `state: present`, `enabled: true`, or a declarative file resource. It is a property of the operation and its implementation, not a guarantee that every Ansible task is automatically idempotent.

A shell or command task may be necessary, but it requires safeguards. The `creates`, `removes`, or `changed_when` controls can help Ansible determine whether an operation should run or whether it changed the system. Poorly written command tasks may report changes on every run, conceal failures, or apply destructive actions repeatedly.

Ansible’s default linear strategy processes task progression across hosts in coordinated batches, although the exact behavior includes forks and synchronization points. The `serial` keyword limits the number of hosts updated at once, which is important for rolling deployments. `max_fail_percentage` and related controls can stop a rollout when too many hosts fail. Delegation can execute a task on another host, while `run_once` restricts a task to one selected host; both require careful reasoning about data and failure scope.

Check mode, invoked with `--check`, estimates changes without applying them where modules support that capability. Diff mode can show file differences, though sensitive content must be protected. These features improve review but cannot perfectly predict operations involving external systems or arbitrary commands.

Connection Methods and Privilege Escalation

The connection plugin determines how Ansible reaches a target. Common methods include:

  • SSH for Unix-like systems.
  • Local execution when no remote connection is required.
  • WinRM or PSRP for Windows systems.
  • Network-specific plugins for routers, switches, and appliances.
  • Container or virtualization connections in specialized environments.

SSH configuration may specify an alternate user, port, private key, proxy jump, or bastion host:

```yaml

web01:

ansible_host: 192.0.2.21

ansible_user: deployer

ansible_ssh_private_key_file: ~/.ssh/automation_ed25519

```

Privilege escalation is separate from connection. The `become` mechanism allows a connection user to obtain additional privileges, commonly through `sudo`:

```yaml

  • hosts: web

become: true

become_method: sudo

tasks:

  • name: Install package

ansible.builtin.package:

name: nginx

state: present

```

The connection account should receive only the privileges required for its tasks. Passwordless privilege escalation may simplify automation but increases the impact of stolen credentials. Organizations should use narrowly scoped sudo policies, short-lived credentials where possible, centralized logging, and approval controls for sensitive operations.

A Detailed Applied Case: Standardizing a Three-Tier Platform

Background

Consider Meridian Services, a fictional organization operating a customer portal with web, application, and database tiers. The company has separate staging and production environments. Systems were provisioned by different teams, resulting in inconsistent package versions, manually edited configuration files, and uncertain service startup behavior.

Situation and Problem

A release succeeded in staging but failed in production because one application host used a different runtime version and a database host had a disabled service. The operations team also maintained a spreadsheet of server addresses, which was frequently outdated. Management required repeatable configuration, controlled production rollout, and an auditable record of changes.

Analysis

The problem was not simply the absence of scripts. It involved three forms of drift: infrastructure membership drift, configuration drift, and process drift. A static list could not reliably identify current cloud instances. Shell scripts lacked consistent change reporting. Finally, no clear boundary existed between environment-specific data and reusable configuration logic.

Approach and Solution

The team implemented a dynamic inventory based on cloud resource tags. Hosts were grouped by environment and service role, such as `staging_web`, `production_web`, and `production_database`. Group variables defined non-sensitive settings, while encrypted variables and a secrets manager supplied credentials.

Reusable roles were created for the operating-system baseline, runtime installation, web service configuration, and database service management. Role defaults contained safe baseline values. Environment-specific variables changed only the required parameters. Tasks used fully qualified module names and declarative states. Configuration templates notified handlers only when files changed.

Production deployment used `serial` to update a small number of web hosts at a time. Health checks were executed between batches, and the playbook stopped if the failure threshold was exceeded. The database role was applied separately because database changes had a different risk profile. Check mode and review of the proposed diff were incorporated into the change process.

Outcome

The organization obtained a reproducible configuration baseline and a clearer separation between inventory data, reusable roles, and environment policy. New hosts entered the correct automation scope through tags. Re-running the playbooks produced no changes on compliant systems, while drift was reported through normal change results. The team also discovered that some application settings had never been documented; these were resolved as part of the design process.

Learning Points

The case demonstrates that Ansible’s value is organizational as well as technical. Inventory is a governance boundary, not merely a host list. Idempotent roles reduce operational uncertainty, but they do not eliminate the need for testing, staged rollout, access control, and human review. Dynamic discovery improves freshness only when resource metadata is governed. Finally, the safest automation separates stable reusable logic from environment-specific data and secrets.

Risks, Limitations, and Common Mistakes

Ansible can produce large-scale changes quickly, which makes incorrect host patterns particularly dangerous. A play intended for `staging_web` may affect production if group names or inventory sources are wrong. Explicit inventory selection, environment protections, approval gates, and limited concurrency reduce this risk.

Common technical mistakes include using shell commands where a module exists, storing secrets in plain text, relying on implicit variable names, ignoring failed tasks with `ignore_errors`, disabling host-key checking without compensating controls, and testing only on one distribution. Another mistake is treating successful task execution as proof of application correctness. Configuration management verifies selected resource states; it does not automatically validate end-to-end business behavior.

YAML syntax can also create errors through indentation, implicit data typing, or ambiguous values. Fully qualified module names, linting, syntax checks, and automated tests help identify problems early. Roles and collections should be versioned and reviewed because a dependency update can alter behavior.

Large inventories may create performance challenges. Fact gathering, API calls, forks, network latency, and controller capacity all affect execution time. Fact caching, appropriate forks, batching, and specialized execution environments can help, but performance tuning should not weaken auditability or safety.

Governance, Security, and Professional Practice

Ansible automation frequently operates with elevated privileges and access to sensitive systems. Governance should therefore cover identity, authorization, code review, credential management, logging, change approval, and separation of duties. Automation repositories should be protected like production code. Branch controls and peer review are particularly important for changes involving destructive modules, firewall rules, user accounts, or secret distribution.

Secrets should be supplied through protected mechanisms such as Vault, an enterprise secrets manager, or a secured automation platform. Encryption at rest is insufficient if playbook output exposes a secret; `no_log: true` may be needed for sensitive tasks, although excessive use can reduce diagnostic value. Logs should be retained according to organizational and regulatory requirements while avoiding unnecessary personal or confidential data.

Compliance automation should be transparent and evidence-oriented. A role that enforces password policy or file permissions should document the intended control, its scope, exceptions, and verification method. Automation should not silently remediate systems where legal, safety, or business requirements demand approval.

Current Developments and Emerging Directions

Ansible is increasingly used through automation platforms that provide web-based inventories, role-based access control, job templates, scheduling, credentials, workflow approval, and centralized execution logs. Execution environments package Ansible, collections, and Python dependencies into reproducible containers, reducing differences between developer machines and CI runners.

Collections provide modular distribution of modules, plugins, and roles. Their use increases reuse but also introduces supply-chain concerns. Organizations should select trusted sources, pin versions, scan dependencies, and test upgrades.

Event-driven automation is another developing direction. Monitoring events can trigger controlled Ansible actions, such as remediation or scaling, but automatic response must include safeguards against alert storms, repeated remediation, and malicious or corrupted event data. AI-assisted operations may help suggest playbooks, interpret failures, or generate task drafts; nevertheless, generated automation requires human review, testing, least-privilege execution, and explicit validation of its assumptions.

Best-Practice Framework for Ansible Projects

A maintainable Ansible implementation generally follows these principles:

1. Keep inventories, playbooks, roles, and configuration under version control.

2. Separate environments and make the selected inventory explicit at execution time.

3. Prefer idempotent modules over unstructured shell commands.

4. Use meaningful groups based on stable operational responsibilities.

5. Keep secrets outside ordinary source files and protect task output.

6. Use role defaults for safe, documented customization points.

7. Validate syntax, lint code, test roles, and use check mode where appropriate.

8. Stage high-risk changes with `serial`, health checks, and rollback planning.

9. Pin important collection and dependency versions.

10. Document privilege requirements, exceptions, ownership, and expected outcomes.

11. Treat dynamic inventory metadata as governed configuration data.

12. Measure success through drift reduction, change reliability, recovery time, and audit quality rather than task count alone.

Chapter Summary

Ansible is an agentless automation platform in which a control node connects to managed systems and executes modules to achieve a desired state. Its practical foundation consists of a reproducible control-node environment, accurate inventories, well-structured variables, facts, connection plugins, privilege escalation, and declarative modules. Inventories identify hosts and organize them into meaningful groups; dynamic inventories can improve freshness but depend on reliable external metadata.

Modules, tasks, plays, and playbooks provide progressively larger units of automation. Idempotence is central because repeated execution should converge systems toward the intended state without unnecessary changes. Ad hoc commands are useful for investigation, while durable operational knowledge should be encoded in tested playbooks and roles. Variable precedence must be managed through disciplined design rather than accidental overrides. Finally, professional Ansible practice requires security controls, governance, staged execution, dependency management, testing, and awareness of the limitations of automation.

Key Terms

Control node: The system from which Ansible commands, playbooks, inventories, and plugins are executed.

Managed node: A target system on which Ansible performs automation tasks.

Agentless automation: Automation that generally does not require a persistent management agent on each target.

Inventory: A collection of managed hosts, groups, and associated variables.

Static inventory: An inventory maintained directly in a file or set of files.

Dynamic inventory: Inventory generated from an external source such as a cloud API or service catalog.

Host pattern: An expression used to select hosts for a command or play.

Module: A reusable unit of Ansible functionality that performs an operation on a target.

Task: A named invocation of a module with specified arguments.

Play: A mapping of tasks to a selected group of hosts.

Playbook: A YAML document containing one or more plays.

Fact: Information automatically gathered about a managed host.

Variable precedence: The ordering rules that determine which variable value overrides another.

Handler: A special task executed when notified, usually after a configuration change.

Idempotence: The property that repeated application of an operation converges on the same desired state.

Connection plugin: The mechanism Ansible uses to communicate with a managed node.

Privilege escalation: A controlled method, such as `sudo`, for obtaining higher permissions.

Ansible Vault: An Ansible feature for encrypting sensitive data used by automation.

Execution strategy: The method governing how tasks progress across multiple hosts.

Drift: Divergence between the actual configuration of a system and its intended configuration.

Review Questions

Short Answer Questions

1. What distinguishes an Ansible control node from a managed node?

2. Why is the Ansible `ping` module not an ICMP connectivity test?

3. What are the principal purposes of an inventory?

4. Define idempotence in the context of configuration management.

5. What is the role of an Ansible handler?

Long Answer Questions

1. Explain Ansible’s agentless architecture and describe the principal stages of playbook execution.

2. Compare static and dynamic inventories, including their benefits, dependencies, and risks.

3. Explain how facts, variables, and variable precedence influence playbook behavior.

4. Discuss the relationship among modules, tasks, plays, playbooks, roles, and handlers.

5. Explain how security, governance, and least privilege should shape an enterprise Ansible implementation.

Analytical / Application Questions

1. An organization has separate staging and production cloud accounts, but resource tags are inconsistent. Design an inventory and governance approach that reduces the risk of applying a production play to staging or vice versa.

2. A playbook reports a configuration file as changed on every run and restarts the application each time. Diagnose the likely design problem and propose an idempotent correction.

3. During a rolling production deployment, two of ten web servers fail configuration. Explain which Ansible execution controls and operational safeguards could limit impact and support recovery.

22. Multiple Choice Questions with Answers

Question 1

Which statement best describes Ansible’s agentless operating model?

A. Every managed host must run a continuously active Ansible daemon.

B. The control node connects to targets and executes modules without normally requiring a persistent agent.

C. Ansible can configure only systems that expose a cloud-provider API.

D. Managed hosts must be rebuilt after every playbook execution.

Correct Answer: B. The control node connects to targets and executes modules without normally requiring a persistent agent.

Question 2

In an inventory, what is the principal purpose of ansible_host when used with an inventory alias such as web01?

A. It defines the operating-system family of web01.

B. It identifies the actual network address to which Ansible connects.

C. It assigns web01 to the production group.

D. It specifies the module used for web01.

Correct Answer: B. It identifies the actual network address to which Ansible connects.

Question 3

Which approach most directly supports idempotent package configuration?

A. Running a shell command that always installs the package again.

B. Using the package module with state: present.

C. Using debug to print the package name.

D. Disabling change reporting for the task.

Correct Answer: B. Using the package module with state: present.

Question 4

What is the main operational purpose of an Ansible handler?

A. To execute a task only when it has been notified, commonly after a change.

B. To define a permanent connection to every managed host.

C. To override all inventory variables.

D. To gather facts before every task.

Correct Answer: A. To execute a task only when it has been notified, commonly after a change.

Question 5

An administrator wants to identify the groups and variables associated with a host before running a playbook. Which command is most appropriate?

A. ansible-inventory --host web01

B. ansible-playbook --syntax-check web01

C. ansible-galaxy --graph web01

D. ansible-config --remove web01

Correct Answer: A. ansible-inventory --host web01

Question 6

A value supplied with extra variables conflicts with a value in group_vars. What should generally be expected?

A. The group_vars value always overrides the extra variable.

B. Both values are merged randomly.

C. The explicitly supplied extra variable generally has higher precedence.

D. The playbook fails before variable resolution.

Correct Answer: C. The explicitly supplied extra variable generally has higher precedence.

Question 7

A team wants to update production web servers gradually and stop if failures exceed an acceptable threshold. Which combination is most relevant?

A. serial and failure-threshold controls such as max_fail_percentage

B. debug and gather_facts: false only

C. local connection and ignore_errors

D. An ad hoc command and no inventory groups

Correct Answer: A. serial and failure-threshold controls such as max_fail_percentage

Question 8

Why can dynamic inventory still produce an incorrect automation scope?

A. Dynamic inventory never supports grouping.

B. It depends on the accuracy of external metadata, filters, permissions, and API data.

C. It always ignores cloud resource tags.

D. It can contain only one managed host.

Correct Answer: B. It depends on the accuracy of external metadata, filters, permissions, and API data.

Question 9

Which practice best protects sensitive information in an Ansible project?

A. Store passwords in ordinary group variables and print them for testing.

B. Use protected secret mechanisms, restrict access, and prevent sensitive task output where necessary.

C. Disable host-key checking for every environment.

D. Place credentials in shell commands so they are not in YAML.

Correct Answer: B. Use protected secret mechanisms, restrict access, and prevent sensitive task output where necessary.

Question 10

A command task reports changed on every execution even though the target state is already correct. What is the most appropriate response?

A. Accept the behavior because all command tasks are inherently idempotent.

B. Replace it with a suitable declarative module or add carefully justified change and execution conditions.

C. Add more forks to make the changes faster.

D. Move the command into inventory variables.

Correct Answer: B. Replace it with a suitable declarative module or add carefully justified change and execution conditions.

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 6 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 7
Ansible Playbooks, Roles and Advanced Automation

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

7. Ansible Playbooks, Roles and Advanced Automation

Introduction and Context

Infrastructure as Code is not limited to creating servers, networks, or cloud resources. Once infrastructure exists, it must be configured, secured, updated, monitored, and eventually retired. Terraform is primarily concerned with provisioning and managing infrastructure state, whereas Ansible is commonly used to perform configuration management, application deployment, orchestration, and operational tasks. In a mature DevOps environment, the two tools are often complementary: Terraform creates or changes the target environment, and Ansible establishes the desired operating-system and application state within it.

Ansible represents automation as readable YAML documents called playbooks. A playbook describes which hosts should be affected and which tasks should be executed. Its agentless architecture, reliance on standard remote protocols such as SSH and WinRM, and extensive module ecosystem make it suitable for heterogeneous environments. However, simple task lists are insufficient for large organisations. Sustainable automation requires careful use of handlers, templates, variables, loops, conditions, tags, blocks, error handling, roles, collections, and dynamic inventories.

This chapter examines these capabilities as parts of one design problem: how to build automation that is repeatable, testable, secure, understandable, and adaptable to change. The central concern is not merely whether a playbook works once, but whether it remains trustworthy when executed repeatedly across development, testing, and production environments.

Learning Objectives

After studying this chapter, the learner should be able to:

  • Explain the structure and execution model of Ansible playbooks.
  • Design tasks using modules, variables, facts, loops, conditions, tags, and handlers.
  • Use templates, blocks, rescue sections, and always sections for controlled automation.
  • Organise reusable automation through roles and collections.
  • Select and configure static or dynamic inventories.
  • Apply idempotence, privilege control, validation, testing, and secure secret management.
  • Analyse an operational scenario and design an appropriate Ansible solution.
  • Evaluate the organisational and governance implications of large-scale automation.

The Playbook as an Executable Operations Model

An Ansible playbook is a YAML file containing one or more plays. A play maps a set of hosts to a sequence of tasks and may define variables, privilege escalation, handlers, tags, and execution controls. A minimal playbook is conceptually simple:

```yaml

  • name: Configure web servers

hosts: web

become: true

tasks:

  • name: Install Nginx

ansible.builtin.package:

name: nginx

state: present

  • name: Ensure Nginx is enabled and running

ansible.builtin.service:

name: nginx

enabled: true

state: started

```

The first play targets the inventory group `web`. Each task invokes a module. Modules are the units that perform operations such as installing packages, managing files, creating users, configuring services, or interacting with cloud APIs. Fully qualified collection names, such as `ansible.builtin.package`, improve clarity and reduce ambiguity.

Ansible normally gathers facts before executing tasks. Facts describe the target system, including its operating-system family, network interfaces, processor information, and memory. They can be referenced through variables such as `ansible_facts['os_family']`. Fact gathering is useful for cross-platform automation, although it can be disabled when performance or privacy considerations require it.

A task should express a desired state rather than an imperative sequence whenever the selected module supports that approach. For example, `state: present` communicates that a package must exist. Re-running the task should not reinstall an already present package. This property is called idempotence. It is central to reliable configuration management because playbooks may be executed after failures, during scheduled reconciliation, or as part of continuous delivery.

Variables, Precedence, and Facts

Variables make playbooks adaptable. Values may be defined in inventory files, group variables, host variables, playbooks, roles, or supplied at runtime. Ansible applies a precedence system to determine which value is effective. The precise hierarchy is extensive, but a sound design principle is to keep ordinary configuration in appropriately scoped variable files and reserve high-precedence mechanisms, such as extra variables, for deliberate overrides.

Variables should describe policy or environment-specific data, not conceal complex program logic. For example:

```yaml

app_port: 8080

app_user: orders

app_directories:

  • /opt/orders
  • /var/log/orders

```

Sensitive values should not be placed in ordinary YAML files. Ansible Vault can encrypt variables or complete files, but encryption does not remove the need for access control, rotation, auditability, and careful handling of decrypted values in logs and temporary files.

Handlers and Event-Driven Changes

Handlers are special tasks triggered by notifications. They are useful when a configuration change requires a service restart or reload:

```yaml

  • name: Deploy service configuration

ansible.builtin.template:

src: orders.conf.j2

dest: /etc/orders.conf

mode: '0644'

notify: Reload orders service

handlers:

  • name: Reload orders service

ansible.builtin.service:

name: orders

state: reloaded

```

A handler normally runs once at the end of a play, even if several tasks notify it. This prevents unnecessary repeated restarts. Handlers therefore express a useful distinction between changing a resource and responding to that change. The `meta: flush_handlers` action can force pending handlers to run at a controlled point, although frequent use may make execution harder to reason about.

Building Expressive Tasks

Templates and Configuration Generation

The `template` module renders Jinja2 templates using variables and facts. Templates are more maintainable than embedding long configuration strings in tasks because presentation and control data remain separate.

```jinja2

server {

listen {{ app_port }};

server_name {{ inventory_hostname }};

location / {

proxy_pass http://127.0.0.1:{{ backend_port }};

}

}

```

A production-quality template should have explicit file ownership and permissions, and its syntax should be validated before replacing a live configuration. Many modules provide a `validate` option; otherwise, a validation task can be executed before a handler reloads the service. Templates should also avoid exposing secrets through rendered files unless the file itself is appropriately protected.

Loops and Data-Driven Automation

Loops eliminate repeated tasks and make the intended data model visible:

```yaml

  • name: Create application directories

ansible.builtin.file:

path: "{{ item }}"

state: directory

owner: "{{ app_user }}"

mode: '0755'

loop: "{{ app_directories }}"

```

The variable `item` represents the current element. For dictionaries or structured data, `loop_control` can assign a meaningful label or variable. Excessively complicated loops, however, reduce readability. If a loop contains several unrelated conditions, a role task file or a data transformation may be more appropriate.

Conditions and Platform Variation

The `when` keyword controls whether a task runs. Conditions should generally be written without Jinja2 delimiters:

```yaml

  • name: Install a Debian-specific package

ansible.builtin.apt:

name: unattended-upgrades

state: present

update_cache: true

when: ansible_facts['os_family'] == 'Debian'

```

Conditions are valuable for operating-system differences, optional components, feature flags, and safe rollout rules. They should not be used to disguise an unclear design. When platforms differ substantially, separate task files or role defaults may communicate the distinction better than a single task containing many conditions.

Tags and Selective Execution

Tags allow operators to select a subset of a playbook, for example `--tags hardening` or `--skip-tags database`. They are useful for development, maintenance windows, and emergency operations. Tags must not become a substitute for dependency management. Running a tag in isolation should be safe only when its prerequisites are documented and satisfied. A tag taxonomy should be agreed upon at organisational level so that operators interpret tags consistently.

Blocks, Failure Handling, and Controlled Recovery

A block groups tasks and applies shared conditions, privilege settings, or error handling. The `rescue` section runs if a task in the block fails, while `always` runs regardless of success or failure.

```yaml

  • name: Deploy and verify application

block:

  • name: Copy release artifact

ansible.builtin.copy:

src: orders.tar.gz

dest: /opt/orders/orders.tar.gz

  • name: Restart application

ansible.builtin.service:

name: orders

state: restarted

rescue:

  • name: Record deployment failure

ansible.builtin.debug:

msg: "Deployment failed on {{ inventory_hostname }}"

always:

  • name: Remove temporary marker

ansible.builtin.file:

path: /tmp/orders-deploying

state: absent

```

Error handling requires an explicit operational policy. `ignore_errors: true` may allow a playbook to continue, but it can also conceal a serious failure. `failed_when` and `changed_when` allow a task’s result to be interpreted according to domain-specific rules. These controls should be narrowly scoped and documented. A system that silently continues after a security-hardening failure may be worse than one that stops visibly.

For rolling changes, `serial` limits the number of hosts processed at once. `max_fail_percentage` can stop a rollout when the failure rate becomes unacceptable. `any_errors_fatal` can stop all hosts when a failure on one host invalidates the entire operation. These options turn a task sequence into a deployment strategy and should be selected according to application availability and consistency requirements.

Roles as the Primary Unit of Reuse

A role provides a conventional directory structure for reusable automation. A typical role contains:

```text

roles/orders/

├── defaults/main.yml

├── vars/main.yml

├── tasks/main.yml

├── handlers/main.yml

├── templates/

├── files/

├── meta/main.yml

└── README.md

```

`defaults` contains low-precedence values intended for users to override. `vars` contains role-internal values that should rarely be overridden. `tasks/main.yml` is the role entry point, while `handlers`, `templates`, and `files` hold related implementation assets. `meta` can declare dependencies and metadata.

Roles separate what a component does from the playbook that composes components. A playbook might include roles for common operating-system hardening, a reverse proxy, an application runtime, and a monitoring agent. This separation supports code review, independent testing, versioning, and reuse across teams.

A good role has a narrow responsibility, sensible defaults, documented variables, predictable handlers, and minimal assumptions about its caller. It should not unexpectedly modify unrelated services or overwrite administrator-managed files. Role dependencies must be evaluated carefully: implicit chains can make execution difficult to understand and can introduce version conflicts.

Collections and the Ansible Ecosystem

Collections package modules, plugins, roles, inventory plugins, and documentation under a namespace and name, such as `community.general` or a vendor-specific collection. They provide a distribution mechanism beyond the core Ansible package. A `requirements.yml` file can declare required collections and their versions:

```yaml

collections:

  • name: community.general

version: '>=8.0.0,<9.0.0'

```

Dependencies should be pinned or constrained through a controlled process. Unreviewed collection upgrades may change module behavior or introduce security concerns. Organisations should maintain an approved dependency catalogue, scan dependencies, and test automation against supported Ansible and collection versions.

Inventory: From Static Lists to Dynamic Topology

Inventory maps names to hosts and groups. A static inventory may be adequate for a small laboratory:

```ini

[web]

web01 ansible_host=192.0.2.10

web02 ansible_host=192.0.2.11

[db]

db01 ansible_host=192.0.2.20

```

In cloud and container environments, hosts are frequently created and destroyed. Dynamic inventory plugins query an external source, such as a cloud provider, virtualization platform, or configuration-management database, and construct groups from metadata. Grouping by tags such as `environment=production` and `role=web` enables playbooks to target logical services rather than fixed addresses.

Dynamic inventory improves adaptability but creates dependencies on credentials, APIs, naming conventions, and source availability. It should be tested independently, cached where appropriate, and governed so that an incorrect cloud tag cannot accidentally place a host in a sensitive production group. Inventory is therefore not merely a list; it is an operational model of topology and responsibility.

A Lifecycle for Maintainable Automation

A professional automation change normally passes through several stages. First, the desired state and scope are defined. Next, the author selects appropriate modules and models variables. The implementation is placed in a role or another reusable unit. Linting checks syntax, naming, and common errors. Molecule or equivalent tests can create ephemeral targets and verify convergence, idempotence, and behavior under failure. In a delivery pipeline, the change is reviewed, scanned, tested in a non-production environment, and promoted with an auditable approval process.

Idempotence should be tested by running the automation twice and examining the second run. A well-designed playbook should report few or no changes on the second execution. Imperative shell commands are sometimes necessary, but they should be avoided when a dedicated module exists. If a command is used, `creates`, `removes`, `changed_when`, or a carefully designed check should communicate when a change actually occurred.

Security is part of design rather than a later enhancement. Use least-privilege accounts, controlled privilege escalation, protected Vault keys, restrictive file modes, encrypted transport, and redaction with `no_log` where sensitive task output could appear. `no_log` should be targeted because excessive use reduces operational visibility. Playbooks should be reviewed for command injection, unsafe variable interpolation, insecure temporary files, and accidental disclosure in debugging output.

Applied Case: Standardising a Multi-Tier Application Platform

Background

The following is an illustrative case. A financial-services company operates a customer-order platform consisting of web servers, application servers, and database nodes. Terraform provisions virtual machines in two environments, but post-provisioning configuration is performed manually. Over time, servers differ in package versions, service settings, user permissions, and monitoring configuration.

Situation and Problem

A security review identifies inconsistent SSH settings and missing audit agents. Application releases also require manual edits to reverse-proxy configuration. A failed release on one application server occasionally leaves the service stopped, while operators lack a consistent rollback procedure. The company needs repeatable automation without causing simultaneous outages across all nodes.

Analysis

The environment has three distinct concerns: common operating-system controls, tier-specific configuration, and release orchestration. A single large playbook would create excessive coupling. Static inventory would also become unreliable because Terraform replaces instances during scaling and recovery. The solution therefore requires roles, dynamic inventory, templates, handlers, and controlled rollout parameters.

Approach and Solution

The team creates four roles: `baseline`, `web_proxy`, `orders_app`, and `audit_agent`. The `baseline` role manages approved packages, users, SSH policy, time synchronisation, and file permissions. The proxy role renders configuration from environment variables and notifies a reload handler only when the template changes. The application role installs a versioned artifact, creates directories, manages a system service, and runs a health check. The audit role installs and configures the organisation’s monitoring component.

A cloud inventory plugin groups instances by tags. The playbook applies baseline controls to all managed hosts, then applies tier-specific roles:

```yaml

  • name: Apply common controls

hosts: all

become: true

roles:

  • baseline
  • audit_agent
  • name: Configure application tier gradually

hosts: app

serial: 1

become: true

roles:

  • orders_app

```

The application role uses a block with `rescue` to restore the previous artifact if the health check fails. A handler restarts the service only after a successful configuration change. Tags distinguish baseline, proxy, application, and audit operations, while CI executes linting, role tests, and a second-run idempotence check.

Outcome

The company obtains consistent configuration across newly provisioned and existing hosts. Rolling deployment limits the blast radius of an application failure. Dynamic inventory removes the need to update host addresses manually, and role documentation clarifies which variables are safe to override. The most important improvement is not merely faster deployment; it is the creation of an auditable, repeatable operational process.

Learning Points

The case demonstrates that reusable automation begins with modelling boundaries. Common controls belong in a common role, application behavior belongs in an application role, and rollout policy belongs in the playbook that composes those roles. Error handling, inventory design, testing, and governance must be considered together rather than added independently.

Challenges, Limitations, and Common Mistakes

Ansible’s flexibility can create inconsistency. Common problems include placing too much logic in one playbook, using shell commands where modules exist, relying on undocumented variable precedence, storing secrets in source control, and treating `ignore_errors` as a general recovery mechanism. Another mistake is writing roles that assume a particular distribution, directory layout, or service manager without documenting that assumption.

Performance can decline when fact gathering, serial execution, or highly granular tasks are used across thousands of hosts. Parallelism must be balanced against API rate limits, service capacity, and change risk. Network interruptions and partial execution are unavoidable in distributed systems; playbooks should therefore be safely re-runnable and should provide useful failure information.

Ansible also has boundaries. It is not a replacement for a configuration database, a full deployment approval system, or specialised database migration tooling. It can orchestrate these systems, but responsibility for transactional guarantees and domain-specific correctness remains with the appropriate technology.

Governance, Professional Practice, and Emerging Developments

At enterprise scale, automation is an organisational control. Changes should be versioned, reviewed, traceable to an approved requirement, and executed using controlled identities. Separation of duties may require different permissions for authoring, approving, and running production automation. Audit records should show the playbook version, target inventory, operator or pipeline identity, and result.

Current practice increasingly combines Ansible with Git-based workflows, execution platforms, policy checks, and ephemeral test environments. Event-driven automation can respond to monitoring events, although automated remediation must be bounded to prevent loops and unintended escalation. Ansible content collections encourage sharing and standardisation, while automation analytics can reveal failure rates, duration, drift, and frequently changed components. AI-assisted code generation may accelerate drafting tasks, but generated automation still requires human review for idempotence, security, privilege boundaries, and operational consequences.

Best-Practice Design Principles

1. Express desired state with purpose-built modules whenever possible.

2. Design every change to be safely repeatable.

3. Keep roles cohesive, documented, and minimally coupled.

4. Use defaults for supported customisation and protect internal invariants.

5. Separate secrets from ordinary configuration and restrict their exposure.

6. Validate rendered configuration before reloading services.

7. Use handlers to avoid unnecessary restarts.

8. Make rollout scope explicit with `serial` and failure thresholds.

9. Test syntax, convergence, idempotence, failure paths, and supported platforms.

10. Treat inventory, dependencies, permissions, and auditability as part of the automation product.

Chapter Summary

Ansible playbooks describe desired configuration and operational workflows through plays and tasks. Modules provide the main mechanism for idempotent change, while variables, facts, templates, loops, conditions, and tags make automation adaptable. Handlers connect configuration changes to controlled service actions. Blocks, rescue sections, and rollout controls provide structured failure management.

Roles organise tasks, templates, handlers, and defaults into reusable units; collections distribute Ansible content and its dependencies. Static inventories suit stable environments, whereas dynamic inventory plugins represent changing cloud and virtualised topologies. Reliable enterprise automation depends on testing, secure secret handling, least privilege, dependency control, versioning, review, and auditability. The professional objective is not simply automation that executes, but automation that remains understandable and safe under repetition, failure, and organisational change.

Key Terms

Idempotence: The property that repeated execution produces the same desired state without unnecessary changes.

Playbook: A YAML document containing one or more Ansible plays.

Play: A mapping between target hosts and a sequence of tasks.

Task: A named unit of work that invokes an Ansible module.

Module: An Ansible component that performs a specific operation on a managed resource.

Handler: A task triggered by notification, normally after ordinary tasks complete.

Template: A Jinja2-based file containing variables and expressions for generated configuration.

Fact: Information gathered about a managed host.

Variable precedence: The rules determining which value is selected when variables have multiple definitions.

Loop: A mechanism for applying a task to multiple data elements.

Condition: A logical expression, commonly expressed with `when`, that controls task execution.

Tag: A label used to select or exclude tasks during execution.

Block: A group of tasks supporting shared control and error-handling sections.

Role: A standardised, reusable directory structure for Ansible automation.

Collection: A distributable package of Ansible modules, plugins, roles, and documentation.

Inventory: The hosts and groups known to Ansible.

Dynamic inventory: An inventory generated from an external system or service.

Ansible Vault: An encryption mechanism for protecting sensitive Ansible data.

Privilege escalation: The controlled execution of tasks with elevated permissions.

Convergence: The process by which repeated automation moves a system toward its desired state.

Review Questions

Short Answer Questions

1. What is the relationship between a play, a task, and a module?

2. Why are handlers preferable to unconditional service restarts?

3. Distinguish between role defaults and role variables.

4. What is the purpose of dynamic inventory?

5. How does idempotence improve operational reliability?

Long Answer Questions

1. Explain how templates, variables, facts, loops, and conditions work together in a cross-platform playbook.

2. Describe the structure of an Ansible role and explain how roles improve maintainability.

3. Discuss blocks, rescue sections, always sections, and rollout controls as mechanisms for handling failure.

4. Explain the security and governance controls required for enterprise Ansible automation.

5. Compare static and dynamic inventories, including their operational benefits and risks.

Analytical / Application Questions

1. Design an Ansible automation structure for a web, application, and database platform. Justify the roles, inventory groups, handlers, and rollout strategy you select.

2. A playbook reports success but leaves a service misconfigured after a template change. Analyse possible design faults and propose validation and error-handling improvements.

3. An organisation’s second playbook run still reports many changes and occasionally restarts every service. Diagnose likely causes and recommend a testing and redesign approach.

22. Multiple Choice Questions with Answers

Question 1

A task installs a package with `state: present` and reports changed only when the package is absent. Which property does this primarily demonstrate?

A. Dynamic inventory

B. Idempotence

C. Privilege separation

D. Template inheritance

Correct Answer: B. Idempotence

Question 2

Why is a handler generally preferred for restarting a service after a configuration change?

A. It runs before all ordinary tasks

B. It runs once after notifications, avoiding unnecessary repeated restarts

C. It ignores failures automatically

D. It encrypts the configuration file

Correct Answer: B. It runs once after notifications, avoiding unnecessary repeated restarts

Question 3

Which design is most appropriate for a cloud environment where hosts are frequently replaced?

A. A manually maintained list of IP addresses

B. A dynamic inventory plugin using host metadata

C. A separate playbook for every individual host

D. Hard-coded hostnames inside templates

Correct Answer: B. A dynamic inventory plugin using host metadata

Question 4

What is the main purpose of the `rescue` section in an Ansible block?

A. To execute tasks when a task in the block fails

B. To define role defaults

C. To gather facts more quickly

D. To force all tasks to run in parallel

Correct Answer: A. To execute tasks when a task in the block fails

Question 5

A role contains a default value that users are expected to override for different environments. Where should that value normally be placed?

A. `defaults/main.yml`

B. `handlers/main.yml`

C. `meta/main.yml` only

D. The inventory plugin source code

Correct Answer: A. `defaults/main.yml`

Question 6

Which approach best reduces the risk of replacing a live service configuration with invalid content?

A. Always restart the service before rendering the template

B. Validate the rendered configuration before notifying a reload handler

C. Use `ignore_errors: true` on every task

D. Put the configuration in a debug message

Correct Answer: B. Validate the rendered configuration before notifying a reload handler

Question 7

An operator uses `ignore_errors: true` on a security-hardening task. What is the principal danger?

A. The task will always execute twice

B. A serious failure may be concealed while the play continues

C. The inventory will become dynamic

D. Handlers will be encrypted

Correct Answer: B. A serious failure may be concealed while the play continues

Question 8

What is the strongest reason to use fully qualified collection names such as `ansible.builtin.package`?

A. They make every task run with root privileges

B. They identify the module’s namespace and reduce ambiguity

C. They eliminate the need for testing

D. They automatically create inventory groups

Correct Answer: B. They identify the module’s namespace and reduce ambiguity

Question 9

A deployment must update one application host at a time and stop if failures exceed an acceptable threshold. Which controls are most relevant?

A. `serial` and failure-threshold settings

B. Only `debug` and `gather_facts`

C. Only `loop` and `with_items`

D. `defaults` and `templates`

Correct Answer: A. `serial` and failure-threshold settings

Question 10

Which practice most directly tests whether an automation design is convergent?

A. Running it once with maximum verbosity

B. Running it twice and examining whether the second run makes unnecessary changes

C. Replacing modules with shell commands

D. Removing all handlers

Correct Answer: B. Running it twice and examining whether the second run makes unnecessary changes

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 7 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 8
Integrating Terraform and Ansible

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

8. Integrating Terraform and Ansible

Introduction and Context

Infrastructure as Code (IaC) is often implemented through more than one automation technology. Terraform is primarily concerned with provisioning and managing infrastructure resources, while Ansible is primarily concerned with configuring operating systems, deploying software, and orchestrating operational tasks. Used together, they can provide a powerful separation of responsibilities: Terraform creates the required infrastructure, and Ansible prepares that infrastructure to run applications.

The integration is not simply a matter of placing two commands in sequence. It requires decisions about ownership, state, dependencies, inventory generation, credentials, failure recovery, and lifecycle management. Poorly designed integration can produce configuration drift, race conditions, duplicated responsibilities, insecure secrets, and pipelines that are difficult to troubleshoot. Well-designed integration creates a predictable chain from infrastructure definition to a usable application platform.

This chapter examines the architecture and practice of combining Terraform and Ansible. It focuses on inventory hand-offs, Terraform outputs, orchestration workflows, dependency management, security, testing, and alternatives such as cloud-init and image-based provisioning. The discussion assumes familiarity with Terraform resources, variables, modules, state, Ansible playbooks, roles, inventories, and idempotence.

Learning Objectives

By the end of this chapter, students should be able to:

1. Explain the distinct responsibilities of Terraform and Ansible in a combined IaC workflow.

2. Design reliable hand-offs between Terraform outputs and Ansible inventories.

3. Construct orchestration workflows with explicit dependencies and readiness checks.

4. Evaluate when to use Ansible, cloud-init, immutable images, or a combination of these approaches.

5. Identify security, governance, and operational risks in multi-tool automation.

6. Apply integration patterns to a realistic infrastructure delivery scenario.

Establishing Clear Tool Boundaries

Terraform uses a declarative model to describe the desired state of infrastructure. It communicates with providers such as AWS, Azure, Google Cloud, Kubernetes, or virtualization platforms. Typical Terraform responsibilities include creating networks, subnets, firewalls, load balancers, virtual machines, databases, identity assignments, and DNS records. Terraform records the relationship between configuration and deployed objects in its state, enabling planning and controlled updates.

Ansible generally operates after a host or service endpoint exists. It connects through SSH, WinRM, network APIs, or other supported mechanisms and applies configuration through modules and roles. Typical Ansible responsibilities include installing packages, creating users, managing files, configuring services, deploying application artifacts, registering monitoring agents, and performing rolling updates.

The boundary can be expressed as follows:

  • Terraform answers: “Which infrastructure resources should exist, and how should they be connected?”
  • Ansible answers: “How should an existing host or service be configured and operated?”

This boundary is a design principle rather than an absolute rule. Terraform can invoke provisioners, and Ansible can interact with cloud platforms. However, frequent use of Terraform provisioners to perform configuration usually weakens state management and makes failures harder to recover. Similarly, using Ansible to create every underlying network resource may obscure infrastructure dependencies and reduce the benefits of Terraform’s planning model.

A useful ownership matrix assigns each concern to one authoritative tool. For example, Terraform may own a virtual machine’s size, subnet, security group, and attached disk, while Ansible owns operating-system packages, configuration files, and service states. If both tools manage the same file, tag, user, or service property, their desired states may conflict. Clear ownership reduces this risk.

The Integration Model: Provision, Discover, Configure, Validate

A mature Terraform–Ansible workflow can be understood as four connected stages.

Provision

Terraform creates or changes infrastructure. The workflow should use a controlled backend for state, a reviewed plan, and appropriate locking. Resources should be organized into modules with meaningful outputs. Provisioning should also establish metadata that later supports discovery, such as tags, labels, instance roles, environment names, and application identifiers.

Discover

Ansible requires an inventory. The inventory identifies hosts, connection details, groups, variables, and sometimes host-specific credentials or policies. Discovery can be performed through Terraform outputs, a dynamically generated inventory file, a cloud inventory plugin, or an external service registry.

Configure

Once hosts are reachable and sufficiently initialized, Ansible applies roles and playbooks. Configuration should be idempotent: running the playbook repeatedly should converge the host toward the same desired state without unnecessary changes.

Validate

The workflow should verify both infrastructure and configuration. Terraform can confirm that resources were created according to its model, while Ansible can validate service availability, configuration syntax, ports, application health, and security settings. Validation is essential because successful resource creation does not imply successful service readiness.

Inventory Hand-offs from Terraform to Ansible

The inventory hand-off is the central technical problem in many integrations. Terraform knows which resources it created; Ansible needs a structured representation of the hosts it must configure. Several patterns are available.

Static Inventory Generated from Outputs

Terraform can expose addresses, names, and roles through output values. A pipeline can then transform those outputs into an Ansible inventory. A simple Terraform output might be:

```hcl

output "web_private_ips" {

value = aws_instance.web[*].private_ip

}

output "web_public_ips" {

value = aws_instance.web[*].public_ip

}

```

The pipeline can obtain machine-readable output with:

```bash

terraform output -json > terraform-outputs.json

```

A script can convert the JSON into an inventory format such as:

```ini

[web]

web-01 ansible_host=203.0.113.10

web-02 ansible_host=203.0.113.11

[web:vars]

ansible_user=automation

```

This pattern is straightforward and useful for ephemeral environments. Its weakness is that the generated file can become stale if infrastructure changes without the inventory being regenerated. The inventory should therefore be treated as a build artifact, not as a manually maintained source of truth.

Terraform-Generated Inventory Templates

Terraform’s `templatefile` function can create a file from resource attributes. This is convenient for small systems, but the generated file is written during Terraform execution and may introduce unwanted local side effects. It can also blur the boundary between provisioning and configuration. When used, it should be limited to producing data for the next stage rather than executing Ansible directly.

Dynamic Inventory Plugins

Ansible dynamic inventory plugins query a cloud provider or another inventory system at execution time. Hosts are grouped using tags, labels, filters, or resource attributes. For example, hosts tagged `Role=web` and `Environment=staging` can be selected without producing a file.

Dynamic inventory is often preferable in long-lived environments because it reflects current infrastructure and avoids stale generated files. It requires reliable cloud authentication, carefully designed tags, and explicit filters. Broad filters can accidentally configure production hosts when a staging playbook is executed, so environment boundaries must be enforced.

Service Discovery and External Inventories

In larger organizations, a configuration management database, service registry, cloud asset inventory, or platform API may serve as the authoritative source. Terraform registers resource metadata, and Ansible queries the external system. This approach supports multi-team environments but introduces another system whose availability, ownership, and data quality must be governed.

Terraform Outputs as Contracts

Outputs are more than convenient print statements. In an integrated workflow, they form an interface between Terraform and downstream automation. A good output contract is stable, minimal, typed, and semantically meaningful.

Useful outputs may include:

  • hostnames or addresses;
  • resource identifiers;
  • role or environment groupings;
  • load-balancer endpoints;
  • secret references rather than secret values;
  • connection metadata required by Ansible.

Outputs should avoid exposing sensitive values unnecessarily. Terraform supports marking outputs as sensitive, but sensitivity mainly affects display; it does not automatically encrypt every location in which state or output data may exist. State backends, CI logs, artifacts, and pipeline variables must also be protected.

The output contract should be versioned conceptually. If a downstream playbook expects `web_private_ips` and the Terraform module later changes that output to a differently structured object, the integration may fail. Module interfaces should therefore be reviewed like software APIs.

Dependency Management and Readiness

Terraform’s dependency graph determines the order in which resources are created and destroyed. Explicit references normally create dependencies automatically. For example, a virtual machine referring to a subnet ID will depend on that subnet. The `depends_on` argument should be reserved for genuine dependencies that Terraform cannot infer from an expression.

An important distinction exists between resource creation and service readiness. A cloud API may report that a virtual machine is running while the operating system is still booting, cloud-init is still executing, or SSH is not yet available. Starting Ansible immediately can therefore produce intermittent failures.

Readiness can be handled through several methods:

1. Terraform can expose the host only after its resource is created, while the pipeline performs a connection retry loop.

2. Ansible can use `wait_for_connection` before configuration.

3. A load balancer or service health endpoint can be checked before subsequent application steps.

4. Cloud-init can perform early boot tasks and signal completion through a file or service state.

Readiness checks should be bounded by timeouts and should produce useful diagnostic information. Indefinite retries hide genuine failures and increase deployment duration.

Orchestration Workflows

A common CI/CD workflow contains the following logical sequence:

```text

Validate Terraform and Ansible

↓

Terraform plan

↓

Approval, where required

↓

Terraform apply

↓

Collect outputs or query dynamic inventory

↓

Wait for host and service readiness

↓

Run Ansible syntax checks and playbooks

↓

Run smoke tests and security checks

↓

Publish logs, outputs, and deployment metadata

```

The orchestration layer may be a CI platform, a deployment controller, or a dedicated workflow engine. The essential principle is that Terraform and Ansible should be separate stages with explicit data exchange, rather than hidden shell commands embedded unpredictably in resource definitions.

A basic pipeline might use:

```bash

terraform init

terraform validate

terraform plan -out=tfplan

terraform apply tfplan

terraform output -json > outputs.json

ansible-playbook -i inventory-generated.yml site.yml

```

In production, this sequence should also include backend configuration, credentials obtained through an identity system, concurrency controls, artifact retention, approvals, and failure handling. The pipeline should record which Terraform commit, Ansible commit, variable set, and environment were used.

Terraform’s `local-exec` and `remote-exec` provisioners can technically invoke commands or connect to hosts. They are best treated as exceptional mechanisms. Provisioners are not modeled as normal infrastructure resources, may not be idempotent, and can behave poorly during replacement or partial failure. External orchestration provides clearer stage boundaries and more observable execution.

Configuration Strategies: Ansible, cloud-init, and Images

Terraform and Ansible are not the only provisioning choices. The appropriate approach depends on the lifecycle and scale of the environment.

Ansible After Provisioning

Ansible is suitable when configuration changes frequently, hosts must be managed after creation, or teams need a flexible operational automation tool. It is strong for multi-step procedures, conditional logic, service orchestration, and heterogeneous operating systems. The cost is that a newly created host depends on a post-provisioning process before it becomes useful.

cloud-init and User Data

cloud-init runs during early operating-system boot and is widely supported by cloud images. It can create users, install initial packages, write files, configure SSH, and execute bootstrap commands. It is useful for establishing connectivity and installing a lightweight agent that completes configuration later.

cloud-init is less suitable for complex, long-lived configuration because debugging early boot scripts can be difficult, execution timing varies by image, and large scripts become hard to maintain. It is often best used for bootstrap rather than as the complete configuration-management system.

Image-Based Provisioning

An image pipeline creates a machine image containing the operating system, packages, agents, and baseline configuration before deployment. Tools such as Packer are commonly used for this purpose. Terraform then launches instances from a known image, possibly followed by a small Ansible step for environment-specific configuration.

Immutable or image-based infrastructure improves startup speed, consistency, and rollback. It also moves complexity into image creation and requires disciplined image versioning, vulnerability scanning, testing, and retirement. Secrets and environment-specific values should generally not be baked into reusable images.

A Decision Framework

Use Ansible after provisioning when configuration is dynamic or operationally rich. Use cloud-init for first-boot bootstrap and minimal initialization. Use images when rapid, repeatable startup and immutable replacement are priorities. Many production systems combine all three: an image supplies the baseline, cloud-init establishes identity and connectivity, and Ansible performs environment-specific configuration.

Detailed Applied Case: A Multi-Tier Application Platform

Background

This illustrative case concerns Northstar Retail, an organization migrating a customer-order application to a public cloud. The platform contains a web tier, an application tier, and a managed database. Development, staging, and production environments must be created consistently, while production changes require approval and audit evidence.

Situation and Problem

The infrastructure team initially used Terraform to create virtual machines and then relied on engineers to configure them manually. This produced differences between environments, delayed releases, and made it difficult to determine which configuration belonged to which application version. A first attempt to run Ansible through Terraform provisioners also caused failures when a virtual machine was replaced: Terraform reported a partial error, while the configuration process had already made untracked changes.

Analysis

The teams mapped ownership. Terraform would manage networks, security groups, instance groups, load balancers, and database resources. Ansible would manage operating-system hardening, runtime packages, application configuration, logging agents, and service units. The teams also recognized that private addresses were preferable for host-to-host communication, while the CI runner required controlled network access through a private connectivity mechanism.

Approach and Solution

Terraform modules were created for networking, compute, and the load-balancing layer. Each instance received tags for environment, application, and role. Outputs exposed grouped instance identifiers and private addresses, but no database password. The password was stored in a managed secrets service, and Ansible retrieved it at runtime using an approved identity.

The CI workflow performed the following actions:

1. Validated Terraform and Ansible code.

2. Generated and reviewed a Terraform plan.

3. Applied the approved plan using a remote, locked state backend.

4. Queried the resulting outputs and generated an ephemeral inventory.

5. Used Ansible’s connection wait mechanism to confirm SSH readiness.

6. Applied hardening and common roles.

7. Applied web and application roles in separate playbooks.

8. Performed health checks through the load balancer.

9. Published test results and deployment metadata.

The web tier was configured in parallel where possible, but application rollout was performed in controlled batches. Ansible handlers restarted services only when relevant configuration changed. The pipeline stopped before application deployment if baseline hardening failed.

Outcome

The environment became reproducible, and a new staging environment could be created without manual host preparation. When an instance was replaced, it was reintroduced through the same inventory and playbook process. The separation between Terraform state and Ansible configuration reduced ambiguity during troubleshooting. The organization retained Ansible for operational changes while adopting image creation for common packages to reduce bootstrap time.

Learning Points

The case demonstrates that integration succeeds when ownership is explicit, outputs are treated as interfaces, and readiness is tested separately from resource creation. It also shows that security requirements influence architecture: secret references, private connectivity, identity-based access, and audit records are part of the workflow rather than additions made after automation is complete.

Risks, Limitations, and Common Mistakes

A frequent mistake is allowing both tools to manage the same concern. For example, Terraform may write a configuration file while Ansible later replaces it. The next Terraform run may not detect the operational change, creating conflicting authorities.

Another mistake is assuming that a successful `terraform apply` means the application is ready. Infrastructure APIs often report resource existence before operating-system initialization or application startup is complete.

Other recurring risks include:

  • generating inventories from stale outputs;
  • placing passwords in Terraform outputs, state, logs, or source control;
  • using broad dynamic-inventory filters;
  • running Ansible with excessive privileges;
  • failing to pin provider, collection, module, and image versions;
  • ignoring partial failures and rerunning blindly;
  • allowing concurrent applies against the same environment;
  • treating generated configuration as an undocumented side effect;
  • embedding large scripts in user data without testing or observability;
  • neglecting drift detection after emergency manual changes.

Recovery design is especially important. If Terraform succeeds but Ansible fails, the environment may contain valid infrastructure that is not a valid application platform. Pipelines should preserve logs, identify the failed stage, support safe reruns, and provide a documented rollback or remediation procedure. Rerunning an idempotent Ansible playbook is normally safer than manually undoing individual commands.

Security, Governance, and Professional Practice

Integration expands the security boundary because the workflow handles infrastructure credentials, host credentials, cloud APIs, and potentially application secrets. Short-lived identity credentials are preferable to long-lived static keys. CI runners should receive only the permissions required for the environment and stage they operate.

Terraform state must be stored in a secured backend with encryption, access control, versioning, and locking. Sensitive outputs should be minimized, and logs should be inspected for accidental disclosure. Ansible Vault or an external secret manager can protect sensitive variables, but encryption at rest does not replace access governance or rotation.

Governance should include code review, policy checks, separation of duties where appropriate, approval for production changes, and traceable artifact versions. Policies can restrict public network exposure, unencrypted storage, privileged access, or unapproved images. Compliance evidence should show what was changed, by whom or which workload identity, and with which versions.

Operationally, teams should define whether configuration drift is corrected automatically, reported for review, or tolerated for specific resources. The answer may differ between a disposable development environment and a regulated production system.

Emerging Directions

The boundary between provisioning and configuration is evolving. Immutable infrastructure and image pipelines reduce the amount of post-provisioning work. Kubernetes and other platforms increasingly expose declarative APIs that can become the primary configuration layer for applications, while Terraform manages the platform around them. Event-driven automation can trigger Ansible or other runbooks when assets change, vulnerabilities are detected, or health signals degrade.

Policy as code is also becoming central. Terraform plans, images, Ansible content, and deployment manifests can all be evaluated against organizational rules before execution. Platform engineering teams increasingly provide approved modules, images, roles, and workflow templates so that application teams consume standardized capabilities rather than assembling unsafe integrations independently.

These developments do not eliminate the need for tool boundaries. They make those boundaries more important because multiple declarative systems may otherwise compete to manage the same resource.

Best Practices for Integrated Delivery

1. Assign one authoritative owner to each infrastructure or configuration concern.

2. Keep Terraform provisioning and Ansible configuration as observable pipeline stages.

3. Treat Terraform outputs and inventories as versioned interfaces or ephemeral artifacts.

4. Prefer dynamic inventory for environments whose membership changes frequently, with strict filters.

5. Separate resource creation, host readiness, service readiness, and application validation.

6. Use tags, labels, and naming conventions consistently across modules and inventories.

7. Keep secrets out of outputs and retrieve them through controlled identity-aware mechanisms.

8. Design Ansible roles to be idempotent, composable, testable, and minimally privileged.

9. Pin versions and test upgrades in isolated environments.

10. Record deployment provenance and provide safe recovery procedures for partial failures.

11. Use cloud-init for bootstrap and images for stable baselines rather than forcing Ansible to perform every task.

12. Review the entire workflow for security, compliance, and unintended cross-environment access.

Chapter Summary

Terraform and Ansible complement one another when their responsibilities are deliberately separated. Terraform is generally responsible for the lifecycle of infrastructure resources, while Ansible configures and operates hosts and services after they exist. Integration depends on reliable inventory hand-offs, stable output contracts, explicit orchestration, readiness checks, and secure handling of state and credentials.

Generated inventories, dynamic inventory plugins, and external discovery systems each have appropriate uses. Terraform outputs should expose only the information required by downstream automation and should not become a channel for secret distribution. External orchestration is usually clearer and more recoverable than invoking Ansible through Terraform provisioners.

Cloud-init and image-based provisioning provide alternatives or complements to Ansible. The choice depends on startup requirements, configuration volatility, immutability goals, and operational maturity. Across all approaches, the central professional principle is to establish clear ownership, test the complete workflow, and design for partial failure and auditability.

Key Terms

Terraform: A declarative tool for provisioning and managing infrastructure resources.

Ansible: An automation tool used to configure systems, deploy software, and orchestrate operational tasks.

Inventory: The collection of hosts, groups, connection parameters, and variables used by Ansible.

Dynamic inventory: An inventory obtained at execution time from a cloud provider or external system.

Terraform output: A named value exposed by a Terraform configuration for users or downstream processes.

State: Terraform’s record of managed resources and their relationship to configuration.

Output contract: The agreed structure and meaning of data passed from Terraform to another tool.

Idempotence: The property that repeated execution converges on the same desired state without unnecessary change.

Provisioning: The creation and modification of infrastructure resources.

Configuration management: The controlled establishment and maintenance of software and operating-system settings.

Readiness check: A test confirming that a resource, host, or service is available for the next workflow stage.

cloud-init: A first-boot initialization system commonly used with cloud virtual machines.

Immutable infrastructure: An approach in which deployed instances are replaced rather than modified extensively in place.

Image-based provisioning: Deploying resources from prebuilt machine images containing a tested baseline.

Drift: A difference between declared or managed state and the actual state of a system.

Provisioner: A Terraform mechanism for executing commands during resource lifecycle operations.

Remote state backend: A shared service that stores Terraform state and may provide locking and versioning.

Policy as code: Machine-readable rules that automatically evaluate infrastructure or configuration changes.

Configuration drift: Unplanned divergence of a configured host or service from its intended state.

Review Questions

Short Answer Questions

1. What is the usual division of responsibility between Terraform and Ansible?

2. Why are Terraform outputs important in an integrated workflow?

3. What is the difference between resource creation and service readiness?

4. Give two methods of supplying an Ansible inventory from Terraform-managed infrastructure.

5. Why should sensitive values generally not be placed in Terraform outputs?

Long Answer Questions

1. Explain the provision–discover–configure–validate model for integrating Terraform and Ansible.

2. Compare generated inventories, dynamic inventory plugins, and external inventory systems.

3. Discuss the risks of using Terraform provisioners to invoke Ansible and recommend a more maintainable approach.

4. Compare Ansible post-provisioning, cloud-init, and image-based provisioning in terms of lifecycle, maintainability, and operational risk.

5. Design a secure and auditable CI/CD workflow for deploying a multi-tier application using Terraform and Ansible.

Analytical / Application Questions

1. A Terraform apply completes successfully, but Ansible cannot connect to half of the new hosts. Diagnose the likely causes and propose a sequence of readiness and troubleshooting steps.

2. An organization uses both Terraform and Ansible to manage the same cloud instance tags. Analyze the governance problem and redesign ownership boundaries.

3. A company requires new production hosts to become available within two minutes and to be identical across regions. Decide how Terraform, images, cloud-init, and Ansible should be combined, and justify the design.

22. Multiple Choice Questions with Answers

Question 1

Which responsibility most appropriately belongs to Terraform in a Terraform–Ansible workflow?

A. Installing application packages on an operating system

B. Creating networks, subnets, and virtual machines

C. Restarting a service after a configuration file changes

D. Applying operating-system hardening rules

Correct Answer: B. Creating networks, subnets, and virtual machines

Question 2

What is the main purpose of a Terraform output in an integrated workflow?

A. To replace Terraform state locking

B. To store application passwords in a convenient location

C. To expose structured information for users or downstream automation

D. To execute an Ansible playbook automatically inside every resource

Correct Answer: C. To expose structured information for users or downstream automation

Question 3

Why might Ansible fail immediately after Terraform reports that a virtual machine was created?

A. Terraform always deletes the SSH service after creation

B. The machine may exist while the operating system is still booting

C. Ansible can configure only physical servers

D. Terraform outputs cannot contain network information

Correct Answer: B. The machine may exist while the operating system is still booting

Question 4

Which inventory approach is generally most suitable for frequently changing cloud environments when strict filtering is available?

A. A manually edited static inventory stored permanently in source control

B. A dynamic inventory plugin querying cloud metadata

C. A list of hosts copied from an old deployment log

D. A hard-coded inventory embedded in every Ansible role

Correct Answer: B. A dynamic inventory plugin querying cloud metadata

Question 5

Which practice best prevents conflicting management between Terraform and Ansible?

A. Allow both tools to modify the same files so they can correct one another

B. Assign one authoritative owner to each infrastructure or configuration concern

C. Run both tools concurrently against every environment

D. Avoid using version control for generated files

Correct Answer: B. Assign one authoritative owner to each infrastructure or configuration concern

Question 6

Why are Terraform provisioners generally discouraged for complex Ansible execution?

A. They cannot create any infrastructure resources

B. They are not modeled as normal infrastructure resources and may be difficult to recover from

C. They require Ansible to use a graphical interface

D. They automatically expose all secrets in the cloud provider

Correct Answer: B. They are not modeled as normal infrastructure resources and may be difficult to recover from

Question 7

Which approach is most appropriate for storing a database password needed by Ansible?

A. Expose it as a normal Terraform output and print it in CI logs

B. Bake it into a reusable machine image

C. Store it in a managed secret system and retrieve it through controlled identity

D. Place it in a public dynamic inventory file

Correct Answer: C. Store it in a managed secret system and retrieve it through controlled identity

Question 8

A team wants fast-starting, consistently patched hosts across several regions. Which combination is most appropriate?

A. Manual configuration after each virtual machine is created

B. A tested machine image, minimal cloud-init bootstrap, and limited Ansible configuration

C. Only a large untested user-data script

D. A permanently maintained static inventory with no image pipeline

Correct Answer: B. A tested machine image, minimal cloud-init bootstrap, and limited Ansible configuration

Question 9

What does idempotence mean in the context of Ansible configuration?

A. A playbook must run only once

B. Repeated execution should converge on the intended state without unnecessary changes

C. Every task must make a change on every run

D. Ansible must recreate the host before applying configuration

Correct Answer: B. Repeated execution should converge on the intended state without unnecessary changes

Question 10

Which sequence best represents a controlled integrated delivery workflow?

A. Run Ansible, manually create infrastructure, then inspect Terraform state

B. Apply Terraform repeatedly, ignore outputs, and configure hosts manually

C. Validate and plan, apply approved Terraform, discover hosts, check readiness, run Ansible, and validate services

D. Generate an inventory once and reuse it for all environments indefinitely

Correct Answer: C. Validate and plan, apply approved Terraform, discover hosts, check readiness, run Ansible, and validate services

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 8 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 9
Cloud, Containers and Hybrid Infrastructure

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

9. Cloud, Containers and Hybrid Infrastructure

Introduction and Context

Infrastructure as Code (IaC) becomes especially significant when infrastructure is distributed across public clouds, private data centres, container platforms, and managed services. A modern application may use a virtual network in one cloud, a managed database in another, Kubernetes for application execution, an on-premises identity service, and Ansible for operating-system configuration. The technical challenge is not merely creating resources. It is coordinating dependencies, security boundaries, operational ownership, portability, and change management across unlike environments.

Terraform and Ansible address different but complementary layers of this problem. Terraform is primarily a declarative provisioning and infrastructure-lifecycle tool. It represents desired infrastructure in configuration files, maintains state, calculates a dependency graph, and invokes provider APIs. Ansible is primarily an automation and configuration-management tool. It connects to hosts or APIs and performs tasks that bring systems to a desired operational condition. In practice, Terraform may create a virtual network, cluster, instances, storage, and identity bindings, while Ansible configures operating systems, installs agents, deploys applications, and performs post-provisioning tasks.

Cloud and hybrid IaC therefore requires architectural judgement. A configuration that is technically portable may fail to exploit valuable provider capabilities. Conversely, deep dependence on one provider can improve performance and simplicity while increasing switching costs. The central concern of this chapter is how to make these choices deliberately.

Learning Objectives

After studying this chapter, the student should be able to:

1. Explain how Terraform and Ansible are applied to public-cloud, private-cloud, container, and hybrid infrastructure.

2. Design IaC for networking, identity, storage, managed services, and Kubernetes.

3. Distinguish portable abstractions from provider-specific capabilities.

4. Evaluate immutable images, configuration management, and the boundary between provisioning and operations.

5. Analyse multi-cloud and hybrid-cloud trade-offs, including security, cost, resilience, governance, and operational complexity.

6. Recommend controls and best practices for reliable, secure, and auditable cloud infrastructure.

A Layered View of Cloud Infrastructure

Cloud infrastructure is best understood as a set of dependent layers rather than a collection of isolated resources. A typical sequence is:

  • Organisational layer: accounts, subscriptions, projects, billing boundaries, policies, and quotas.
  • Identity layer: users, service accounts, roles, groups, workload identities, and privileged-access controls.
  • Network layer: virtual networks, subnets, routes, gateways, firewalls, load balancers, DNS, and connectivity to external networks.
  • Compute layer: virtual machines, autoscaling groups, serverless functions, and container clusters.
  • Data layer: block, object, and file storage; databases; queues; caches; and backup services.
  • Platform layer: Kubernetes, service meshes, ingress controllers, observability systems, and deployment platforms.
  • Workload layer: applications, configuration, secrets, and runtime policies.

The order is not always strictly linear. Identity policies may be required before resources can be created, while DNS and certificates may depend on load balancers. IaC tools model these relationships through explicit references, implicit dependencies, modules, inventories, and execution order.

A sound design assigns ownership to the appropriate layer. Terraform is generally well suited to durable resources whose lifecycle should be reviewed through a plan. Ansible is well suited to host configuration, software installation, procedural orchestration, and tasks that cannot conveniently be represented as cloud resources. Kubernetes manifests or Helm charts may manage resources inside a cluster, but they should not automatically be used to manage the entire cloud foundation.

Terraform Across Public and Private Clouds

Terraform uses providers as adapters to APIs. A provider exposes resources and data sources, while the Terraform engine maintains a dependency graph and state. The same language can describe resources from different providers, but syntactic similarity does not imply semantic equivalence. A virtual network, security group, managed database, or identity role may have substantially different behaviours in different clouds.

A practical Terraform architecture commonly separates:

  • reusable modules for networks, clusters, databases, and monitoring;
  • environment-specific root modules for development, testing, and production;
  • variable and output interfaces between modules;
  • remote state with locking and encryption;
  • provider configuration and version constraints;
  • policy checks and automated plan review.

A module should expose meaningful inputs such as network address ranges, availability requirements, or database size, rather than exposing every provider argument. Excessive abstraction produces a lowest-common-denominator module that hides important differences. A useful module abstracts repeated design decisions while still allowing deliberate escape hatches for provider-specific features.

Private-cloud platforms may expose APIs through technologies such as VMware, OpenStack, or platform-specific tools. Terraform can provision these environments when a suitable provider exists, although provider maturity, API consistency, and resource coverage must be assessed. Ansible can then configure virtual machines, network appliances, and platform services. In a private cloud, IaC must also account for physical capacity, IP address management, hardware failure domains, licensing, and operational teams—concerns that public-cloud abstractions may conceal.

Networking as an IaC Design Problem

Networking is often the foundation on which other resources depend. A production design normally specifies address spaces, subnet segmentation, routing, ingress and egress controls, private service access, DNS, load balancing, and connectivity to corporate networks.

Terraform is appropriate for declaring networks and their durable relationships. For example, a module may create a virtual network, private application subnets, restricted data subnets, route tables, security groups, and private endpoints. The configuration should make trust boundaries visible. A database subnet should not become reachable merely because an application subnet and a management subnet share a broad rule.

Hybrid connectivity introduces additional dependencies: virtual private networks or dedicated links, route exchange, firewall policies, overlapping address spaces, and DNS forwarding. IaC should validate these assumptions before deployment. A useful practice is to maintain an explicit network design document or machine-readable contract that records CIDR ranges, owners, permitted flows, and failure domains.

Ansible can configure network appliances or operating-system firewalls, but it is risky to use ad hoc scripts as the sole record of network intent. Configuration drift becomes difficult to detect when changes are made directly through consoles or device-specific commands. Where possible, both cloud network resources and appliance configuration should be represented in version control and tested in a safe environment.

Identity, Secrets, and Governance

Identity is infrastructure. A cloud deployment is incomplete if it creates compute resources without defining who or what may administer, read, modify, or delete them. IaC should establish least-privilege roles, service identities, group assignments, resource policies, and audit settings.

Long-lived access keys embedded in Terraform variables or Ansible inventories are a serious design weakness. Prefer short-lived credentials, federated identity, workload identity, and secret-manager integration. A CI/CD pipeline should authenticate using a controlled identity with narrowly scoped permissions. Secrets should not be stored in source code, Terraform state in plaintext, command output, or unprotected artifacts. State itself may contain sensitive attributes and must therefore be encrypted, access-controlled, backed up, and retained according to policy.

Governance can be implemented through policy as code. Rules may prohibit public storage buckets, require encryption, restrict geographic locations, mandate tags, or prevent excessively broad firewall rules. These policies should run before apply, not merely after an incident. Tagging and naming standards also support cost allocation, ownership, incident response, and automated cleanup.

Storage and Managed Services

Cloud storage has different durability, availability, performance, and consistency properties. IaC must distinguish block storage attached to a compute instance, shared file storage, object storage, and managed databases. The configuration should specify encryption, backup, retention, deletion protection, replication, and recovery objectives rather than treating storage as an anonymous volume.

Managed services reduce operational work but introduce provider dependence. A managed database may offer automated patching, backups, scaling, and monitoring, yet its extensions, network model, export tools, or replication features may not be portable. The decision should compare total operational cost and risk, not merely the monthly service price.

Destructive operations deserve particular attention. Terraform may correctly plan the replacement of a database when an immutable attribute changes, but correctness does not make the operation acceptable. Production resources should use deletion protection, lifecycle rules, backup validation, and approval gates. Data migration and rollback procedures should be tested independently of the IaC code.

Immutable Images and Configuration Management

An immutable infrastructure approach creates a machine image containing the operating system, packages, baseline configuration, and agents. Instances are replaced rather than manually modified. Image pipelines can use tools such as Packer, after which Terraform provisions instances from approved image versions. This improves repeatability, reduces configuration drift, and makes rollback more predictable.

Immutability does not mean that no runtime configuration exists. Environment-specific values, certificates, feature flags, and secrets are usually injected at deployment or startup. Ansible remains valuable for image construction, post-provisioning, controlled operational tasks, and systems that cannot be replaced easily. However, using Ansible to make frequent, undocumented changes to long-lived production servers weakens the benefits of immutable design.

A useful boundary is:

  • Terraform: infrastructure resources and their lifecycle.
  • Image pipeline: operating-system and common software baseline.
  • Ansible: host configuration, operational automation, and integration tasks.
  • Kubernetes or deployment tooling: application workloads and release behaviour.

These boundaries are conventions rather than laws. The correct division depends on the organisation’s skills, platform, compliance requirements, and recovery model.

Kubernetes and Container Infrastructure

Containers package applications with their dependencies, but they still require infrastructure: registries, networks, nodes, identity, storage classes, ingress, observability, and security controls. Kubernetes adds an orchestration layer with its own declarative API and reconciliation loops.

Terraform is often used to create the cloud foundation and managed Kubernetes control plane, node pools, identity bindings, registries, and supporting services. Kubernetes providers, Helm, GitOps controllers, or Ansible may then install cluster-level components and applications. Separating these layers reduces the risk that a failed application deployment will modify foundational networking or destroy a cluster.

Container infrastructure must address image provenance, vulnerability scanning, admission policies, resource requests and limits, pod security, network policies, secret handling, and persistent-volume behaviour. A container image is not a security boundary by itself. The host kernel, runtime, registry, admission process, and cloud identity model remain important.

Kubernetes stateful workloads require special care. A persistent volume claim may be declarative, but the underlying storage class, availability zone, backup method, and recovery process are platform-specific. IaC should document which resources are disposable and which contain irreplaceable data.

Hybrid and Multi-Cloud Architecture

Hybrid infrastructure combines on-premises and cloud resources; multi-cloud uses more than one public cloud. These approaches may be justified by regulatory location requirements, acquisition history, specialised services, resilience objectives, customer demand, or negotiation strategy. They are not automatically more resilient. A common identity failure, shared DNS dependency, poorly tested failover process, or central CI/CD outage can affect every environment.

Portability exists at several levels:

1. Syntax portability: similar Terraform or Kubernetes configuration can be reused.

2. API portability: equivalent resource operations are available across platforms.

3. Operational portability: teams can monitor, secure, recover, and troubleshoot each platform consistently.

4. Workload portability: applications and data can move with acceptable effort, performance, and cost.

The first level is easy to achieve and the last is difficult. A Kubernetes workload may deploy on multiple clouds, but storage performance, load-balancer behaviour, identity integration, egress pricing, and managed database dependencies may prevent practical migration.

A decision matrix should evaluate capability, cost, latency, compliance, skills, resilience, exit effort, and operational complexity. Standardising every capability across clouds can reduce portability to a minimal common set and increase engineering effort. Using each cloud’s strongest services may produce a better system but requires specialised skills and creates switching costs. The appropriate strategy is usually selective portability: keep critical interfaces and data-export paths portable while using provider-specific capabilities where their value is substantial.

An Illustrative Case: Northstar Retail’s Hybrid Platform

Background

Northstar Retail operates physical stores, an on-premises warehouse system, and a cloud-based customer platform. The company plans to modernise its order service. Store and warehouse systems must remain connected to the private data centre, while customer-facing services require elastic cloud capacity.

Situation and Problem

The initial proposal uses two public clouds to avoid dependence on one provider. However, the engineering team has limited experience with both platforms. Network ranges overlap with an existing data centre, database replication is untested, and administrators manually create cloud identities and firewall rules. The organisation needs a safer delivery model without promising unrealistic active-active portability.

Analysis

The main risks are not Terraform syntax or container packaging. They are identity inconsistency, data movement, network addressing, recovery time, and insufficient operational expertise. A two-cloud design would double the number of firewalls, monitoring integrations, IAM models, and incident procedures. The business requires recovery within hours, not necessarily simultaneous processing in both clouds.

Approach

Northstar adopts a primary-cloud and warm-recovery architecture. Terraform modules create the primary network, private connectivity, cluster, registry, managed database, logging, and identity bindings. A smaller recovery environment is provisioned in the second cloud using separate modules rather than pretending that all resources are identical. Ansible builds approved images and configures warehouse gateways and monitoring agents.

Application containers follow Kubernetes standards, but the platform team documents provider-specific ingress, storage, and identity mappings. Database backups are exported in a documented format and restored regularly in the recovery environment. Policy checks reject public database endpoints, unencrypted storage, missing ownership tags, and unrestricted administrative ports. Terraform state is stored remotely with locking and restricted access.

Outcome

The company gains repeatable environments and a tested recovery process. It does not achieve effortless workload portability, but it achieves a more realistic objective: the ability to rebuild essential services in a second environment within the approved recovery target. Operational runbooks identify which steps are automated and which require human approval.

Learning Points

The case demonstrates that multi-cloud value depends on tested recovery and organisational capability. Reusing code is helpful, but portable operations, data recovery, identity management, and observability matter more. A deliberately asymmetric architecture can be more economical and reliable than two fully identical environments.

Challenges, Risks, and Common Mistakes

Cloud IaC fails in recognisable ways. Teams may place all environments in one large state file, allowing unrelated changes to block one another and increasing blast radius. They may create overly generic modules that hide important provider behaviour, or use copy-and-paste configurations that drift across environments. Other common mistakes include storing secrets in repositories, allowing manual console changes without reconciliation, failing to pin provider and module versions, and treating a successful plan as proof that an application will operate correctly.

State corruption, provider bugs, API throttling, eventual consistency, quota exhaustion, and partial failure also require operational planning. A pipeline should retain plans and logs, use concurrency controls, and define recovery procedures for failed applies. Drift detection should distinguish authorised emergency changes from unapproved configuration. Importing existing infrastructure into state may be necessary during migration, but imported resources should subsequently be normalised and governed.

The principal limitation of IaC is that it cannot eliminate architectural ambiguity or organisational weakness. Code can reproduce a poor design very efficiently. It also cannot guarantee that external systems, human approvals, data semantics, or operational procedures behave as intended.

Professional, Ethical, and Governance Considerations

Infrastructure decisions affect confidentiality, availability, accessibility, and public trust. Engineers must minimise unnecessary collection and exposure of data, apply retention rules, and consider jurisdictional requirements. Automated deletion or replacement must not be designed without safeguards for data owners. Access should be auditable, and emergency privileges should be time-limited and reviewed.

Professional responsibility also includes honest communication about resilience. Describing a system as “multi-cloud” does not prove independence or failover capability. Recovery claims should be supported by exercises and measured recovery time and data-loss objectives. Cost forecasts should include network egress, managed-service consumption, support plans, licensing, and staff capability. Sustainability considerations include resource right-sizing, autoscaling policies, image lifecycle management, and avoiding unnecessary duplicate environments.

Emerging Directions and Best Practices

Platform engineering increasingly provides approved templates, self-service interfaces, policy guardrails, and standard observability while allowing application teams to work at an appropriate abstraction level. GitOps extends declarative management into continuous reconciliation, particularly for Kubernetes. Infrastructure testing is also becoming more systematic through static analysis, policy testing, ephemeral environments, integration tests, and disaster-recovery exercises.

Artificial intelligence may assist with configuration explanation, anomaly detection, cost analysis, and remediation suggestions. It should not receive unrestricted production credentials or apply infrastructure changes without review. Generated recommendations require validation because cloud context, security requirements, and organisational policy are not always visible to a model.

Effective practice includes:

  • design layers and ownership boundaries before writing modules;
  • keep state remote, encrypted, locked, backed up, and access-controlled;
  • pin versions and review plans through pull requests;
  • use short-lived identities and secret managers;
  • test networks, policies, images, backups, and recovery procedures;
  • prefer immutable replacement for suitable workloads;
  • document provider-specific assumptions and exit paths;
  • separate foundation, cluster, and application lifecycles;
  • monitor drift, cost, security posture, and operational SLOs;
  • measure portability by tested recovery and migration effort, not code reuse alone.

Chapter Summary

Cloud, container, and hybrid infrastructure requires IaC to coordinate several dependent layers: identity, networking, compute, storage, managed services, Kubernetes, and applications. Terraform provides declarative provisioning and lifecycle management across providers, while Ansible supports configuration, image construction, operational automation, and integration. Immutable images reduce drift, but runtime configuration and provider-specific behaviour remain important.

Portability has technical and operational dimensions. Multi-cloud and hybrid designs can support regulatory, resilience, or capability objectives, but they increase complexity and do not automatically provide failover. Sound architecture uses modularity, policy as code, secure state management, tested recovery, selective abstraction, and explicit governance. The most valuable outcome is not identical infrastructure everywhere; it is infrastructure that can be understood, changed, secured, and recovered with confidence.

Key Terms

Infrastructure as Code: Managing infrastructure through machine-readable, version-controlled definitions.

Provider: A Terraform plugin that communicates with a platform or service API.

Terraform state: The persisted mapping between configuration and resources managed by Terraform.

Module: A reusable, parameterised unit of Terraform configuration.

Immutable infrastructure: Infrastructure replaced from approved artefacts rather than modified indefinitely in place.

Configuration drift: Difference between recorded desired configuration and actual infrastructure state.

Managed service: A provider-operated service that reduces customer responsibility for underlying operations.

Virtual network: A logically isolated network defined within a cloud or virtualisation platform.

Workload identity: An identity assigned to an application or service rather than a human user.

Policy as code: Machine-enforced rules governing infrastructure and deployment decisions.

Container: A process package containing an application and its required user-space dependencies.

Kubernetes: A platform for orchestrating containerised workloads through declarative APIs.

Persistent volume: Storage exposed to a workload with lifecycle characteristics beyond a container instance.

Hybrid cloud: An architecture combining private infrastructure with public-cloud services.

Multi-cloud: Use of services from more than one public-cloud provider.

Portability: The ability to move or reproduce workloads across environments with acceptable effort and impact.

State locking: A mechanism preventing conflicting concurrent changes to infrastructure state.

Least privilege: Granting only the permissions required for a task or identity.

Recovery point objective: The maximum acceptable amount of data loss measured in time.

Recovery time objective: The target time for restoring a service after disruption.

Review Questions

Short Answer Questions

1. How do Terraform and Ansible differ in their primary responsibilities?

2. Why should Terraform state be protected as sensitive information?

3. What is meant by selective portability in multi-cloud architecture?

4. State two benefits of immutable infrastructure.

5. Why are Kubernetes persistent workloads more complex than stateless workloads?

Long Answer Questions

1. Explain how IaC can be structured across identity, networking, compute, storage, Kubernetes, and application layers.

2. Discuss the benefits and risks of using managed services in a hybrid-cloud architecture.

3. Compare immutable images with post-provisioning configuration management using Ansible.

4. Analyse the organisational and technical consequences of adopting a multi-cloud strategy.

5. Describe a governance model for secure Terraform and Ansible delivery in production.

Analytical / Application Questions

1. An organisation wants identical Terraform modules for two clouds, but their databases and identity systems behave differently. Propose an abstraction strategy and justify where provider-specific code should remain.

2. A Terraform plan proposes replacing a production database after a configuration change. Explain the checks, approvals, safeguards, and recovery tests required before proceeding.

3. Design a hybrid recovery approach for a containerised application whose primary database and network connectivity are located in a private data centre.

22. Multiple Choice Questions with Answers

Question 1

Which responsibility most naturally belongs to Terraform in a layered cloud IaC design?

A. Installing a package interactively on a running server

B. Creating a virtual network, subnets, and managed database

C. Performing a one-time application data correction

D. Responding manually to an application alert

Correct Answer: B. Creating a virtual network, subnets, and managed database

Question 2

Why should Terraform state be encrypted and access-controlled?

A. It contains only public provider documentation

B. It may contain sensitive attributes and defines managed infrastructure relationships

C. Encryption is required only for Kubernetes manifests

D. State is used solely to calculate application logs

Correct Answer: B. It may contain sensitive attributes and defines managed infrastructure relationships

Question 3

What is the principal danger of an overly generic multi-cloud module?

A. It prevents version control from being used

B. It always increases network bandwidth

C. It may hide important provider-specific behaviour and reduce useful capability

D. It makes Terraform unable to create any resource

Correct Answer: C. It may hide important provider-specific behaviour and reduce useful capability

Question 4

Which approach best represents immutable infrastructure?

A. Manually patching production servers every week

B. Replacing instances from a tested image when the baseline changes

C. Allowing administrators to modify servers without recording changes

D. Disabling image versioning to simplify deployments

Correct Answer: B. Replacing instances from a tested image when the baseline changes

Question 5

A team wants to use two clouds for resilience. Which evidence most strongly demonstrates that the design provides recovery capability?

A. The same application name exists in both clouds

B. Both clouds have identical colour-coded dashboards

C. A tested recovery exercise meets defined recovery objectives

D. The team has copied Terraform files between repositories

Correct Answer: C. A tested recovery exercise meets defined recovery objectives

Question 6

Which practice best protects a CI/CD pipeline’s cloud access?

A. Embedding a permanent administrator key in the repository

B. Using a short-lived, narrowly scoped workload identity

C. Sharing one personal credential among all engineers

D. Printing credentials in pipeline logs for auditing

Correct Answer: B. Using a short-lived, narrowly scoped workload identity

Question 7

Why should Kubernetes foundation resources and application workloads often have separate lifecycles?

A. Kubernetes cannot run applications on cloud infrastructure

B. It reduces the chance that an application change will alter or destroy foundational infrastructure

C. Terraform cannot manage more than one resource type

D. Applications never require configuration

Correct Answer: B. It reduces the chance that an application change will alter or destroy foundational infrastructure

Question 8

Which statement best distinguishes syntax portability from operational portability?

A. Syntax portability concerns reusable configuration; operational portability concerns the ability to run and recover consistently

B. They are identical because equivalent code guarantees equivalent operations

C. Operational portability applies only to physical servers

D. Syntax portability requires using no provider APIs

Correct Answer: A. Syntax portability concerns reusable configuration; operational portability concerns the ability to run and recover consistently

Question 9

A managed database is convenient but uses provider-specific extensions. What trade-off does this illustrate?

A. Lower operational effort may be accompanied by increased provider dependence

B. Managed services always eliminate the need for backups

C. Provider-specific features guarantee effortless migration

D. Database services cannot be governed through IaC

Correct Answer: A. Lower operational effort may be accompanied by increased provider dependence

Question 10

Which control most directly prevents a public database endpoint from being deployed?

A. A policy-as-code rule evaluated before apply

B. A larger container image

C. A manual naming convention without enforcement

D. A second application deployment

Correct Answer: A. A policy-as-code rule evaluated before apply

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 9 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 10
Testing, Quality and Reliability Engineering

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

10. Testing, Quality and Reliability Engineering

Introduction and Context

Infrastructure as Code (IaC) changes infrastructure from an manually executed operational activity into a versioned, reviewable, and repeatable engineering process. Terraform configurations describe desired resources and relationships, while Ansible playbooks describe configuration and orchestration procedures. This shift creates major benefits, but it also creates a new obligation: infrastructure code must be tested with the same discipline applied to application code.

An infrastructure change can fail in several ways. It may be syntactically invalid, violate organizational policy, produce an incorrect dependency graph, configure a service insecurely, or work in a test account but fail under production constraints. A successful deployment is therefore not sufficient evidence of quality. Quality must be evaluated across multiple dimensions: correctness, security, maintainability, repeatability, recoverability, performance, and operational suitability.

Testing in IaC is not one activity or one tool. It is a layered engineering practice that begins with formatting and validation, progresses through static analysis and policy testing, and extends to unit, contract, integration, and end-to-end testing. Ephemeral environments provide realistic test contexts, while observability, failure injection, and recovery testing assess whether infrastructure remains dependable when conditions are abnormal.

For postgraduate DevOps professionals, the central question is not simply “Does the code run?” It is “What evidence demonstrates that this infrastructure is safe to change, behaves as intended, satisfies organizational constraints, and can be recovered when assumptions fail?”

Learning Objectives

By the end of this chapter, students should be able to:

1. Explain the role of testing and quality engineering in Terraform and Ansible delivery pipelines.

2. Distinguish formatting, validation, linting, static analysis, policy, unit, contract, integration, and end-to-end tests.

3. Design a layered test strategy using Terraform test frameworks and Ansible testing tools.

4. Use ephemeral environments to test infrastructure realistically without creating uncontrolled cost or risk.

5. Interpret test evidence, plan changes, drift signals, and recovery results in engineering decisions.

6. Identify common testing failures, governance concerns, and limitations of infrastructure test automation.

7. Develop quality gates and reliability practices appropriate for professional DevOps environments.

Infrastructure Quality as an Engineering Property

Quality in IaC is multidimensional. Functional correctness asks whether resources and configurations satisfy their declared purpose. Structural quality concerns modularity, naming, dependency clarity, idempotence, and maintainability. Security quality concerns identity, network exposure, secrets, encryption, and compliance. Reliability quality concerns availability, failure tolerance, recovery, and predictable behavior during change.

These dimensions often interact. A configuration may be functionally correct but operationally unsafe because it permits public access. A highly available design may be expensive or unnecessarily complex. A playbook may complete successfully while repeatedly changing the same file, demonstrating a lack of idempotence. Quality engineering therefore requires explicit acceptance criteria rather than an assumption that successful execution equals correctness.

A useful quality model evaluates infrastructure through four questions:

  • Is the change valid? The code is well formed and can be interpreted by the relevant tools.
  • Is the change intended? The plan or execution matches the requested design and does not contain unexplained side effects.
  • Is the change permitted? Security, policy, compliance, cost, and architectural rules are satisfied.
  • Is the change dependable? The resulting system operates, tolerates expected failures, and can be restored or rolled back.

Testing should produce evidence for each question. No single test type can answer all of them.

The Testing Pyramid for Infrastructure

The testing pyramid is a useful model for balancing speed, cost, and realism. Lower-level tests are fast and numerous; higher-level tests are slower, more expensive, and closer to actual user or operator experience.

At the base are formatting, parsing, validation, linting, and static analysis. These tests identify defects before provider calls or remote execution. Above them are unit and module tests, which assess local logic and expected resource construction. Contract tests verify assumptions between modules, modules and providers, or automation and operating systems. Integration tests apply code to real or realistic services and inspect the resulting state. End-to-end tests assess complete workflows, such as provisioning a network, deploying an application, accessing it through its public interface, and destroying the environment.

The pyramid is not a rigid sequence. A security policy check may be fast but conceptually operate at a higher level than syntax validation. Similarly, a small smoke test after deployment may be more valuable than a large collection of shallow unit tests. The principle is to obtain the earliest reliable feedback possible and reserve expensive tests for behavior that cannot be established earlier.

The First Quality Gates: Formatting, Validation and Linting

Formatting establishes a shared representation of code. Terraform’s `terraform fmt` applies canonical formatting, reducing irrelevant differences in reviews and making automated comparison more reliable. Ansible does not have a single equivalent formatting command for every file type, but YAML formatters, editor integrations, and style rules can enforce indentation, quoting, line length, and document consistency.

Validation checks whether a configuration is structurally acceptable. `terraform validate` verifies internal consistency and configuration syntax within an initialized working directory. It does not prove that a design is secure or that a cloud provider will accept every operation. Ansible syntax checks, YAML parsers, and collection validation identify malformed playbooks, undefined structures, and certain module errors.

Linting applies style and heuristic rules. Tools such as TFLint can detect suspicious Terraform constructs, provider-specific issues, and practices that are technically valid but risky. Ansible Lint identifies deprecated modules, command use where an idempotent module is preferable, weak task names, missing handlers, and other maintainability concerns. Linting is valuable because it captures institutional knowledge that is difficult to express through syntax alone.

These gates should run locally and in continuous integration. They should be deterministic, quick, and easy to reproduce. A team should distinguish mandatory correctness failures from advisory style warnings; otherwise developers may learn to ignore all output.

Static Analysis and Policy as Code

Static analysis examines code or planned changes without necessarily creating infrastructure. Terraform plan output can be analyzed by tools such as Checkov, tfsec, or custom policy engines. Open Policy Agent and its Rego language can express rules such as “storage resources must use encryption,” “production subnets must not have direct internet routes,” or “deployments require approved regions.” Sentinel is another policy-as-code approach used in some Terraform-oriented environments.

Policy checks should be specific, explainable, and linked to an owner. A policy that simply rejects a plan without identifying the violated principle creates frustration and encourages workarounds. Exceptions should be explicit, time-limited, documented, and auditable. Policy-as-code is not a replacement for professional judgment; it is a mechanism for making repeatable organizational decisions visible and enforceable.

Static analysis has limitations. It may not understand runtime behavior, organization-specific context, or the effect of data returned by external systems. A rule that blocks every public load balancer may be inappropriate for a public website. Policies therefore require testing themselves, including positive cases, negative cases, and exception cases.

Unit, Contract and Module Testing

A Terraform module can be treated as a unit when its inputs, outputs, resources, and observable behavior are defined clearly. Unit-level testing may inspect generated plans, validate variable constraints, test conditional resources, and confirm output values. Terraform’s native testing framework, invoked through `terraform test`, supports test files that exercise module configurations and can use assertions against planned or applied results. Depending on the test design, tests may use mocked providers or real provider operations; the latter provide stronger environmental evidence but incur cost and side effects.

Unit tests should focus on decisions made inside the module. For example, a network module may test whether enabling a private endpoint creates the expected endpoint resources and whether an invalid CIDR is rejected. A test should not merely repeat the implementation in a different syntax, because such a test can reproduce the same defect.

Contract tests verify interfaces. A module contract may state that a subnet module exports subnet identifiers, that a database module requires a private subnet set, or that an Ansible role expects a variable with a particular type and default. Contract testing is especially important when separate teams own modules and consumers. It detects breaking changes before integration environments are affected.

Ansible role testing commonly uses Molecule. Molecule can create a test instance or container, converge a role, verify the resulting state, and destroy the test instance. Verification may use Ansible assertions, Testinfra, pytest, or platform-specific checks. A strong role test checks both positive behavior and idempotence: after convergence, running the role again should produce no unexpected changes.

Integration and End-to-End Testing

Integration testing applies infrastructure to actual provider APIs or realistic operating systems. It can verify that security groups, identity bindings, routing, managed services, and configuration agents work together. Tools such as Terratest, written in Go, are frequently used to deploy Terraform configurations, inspect cloud resources, execute commands, and destroy the environment. Kitchen-Terraform and other harnesses can provide similar workflows.

Integration tests should define lifecycle boundaries. Setup must be repeatable, test data must be isolated, and teardown must run even when assertions fail. State files, credentials, temporary DNS records, and cloud resources require explicit ownership and cleanup. Tags, labels, and cost budgets help identify abandoned resources.

End-to-end tests evaluate a complete user or operational journey. An example might provision a load balancer, application instances, a database, monitoring, and identity permissions; then verify that a request reaches the application, data persists, alerts are produced, and the environment can be destroyed. End-to-end tests are expensive and can be unstable because they depend on many systems. They should therefore be limited to critical workflows and supported by diagnostic logging and correlation identifiers.

A deployment smoke test is a narrow end-to-end test intended to provide rapid confidence after release. It should test essential availability and basic functionality, not every possible behavior. Smoke testing complements, rather than replaces, deeper integration tests.

Terraform Testing in a Delivery Pipeline

A mature Terraform pipeline commonly follows this sequence:

1. Check formatting and repository conventions.

2. Initialize with controlled provider and module versions.

3. Validate configuration.

4. Run linting and static analysis.

5. Generate a plan using the intended workspace, variables, and backend controls.

6. Evaluate policy against the plan or its normalized representation.

7. Run module and contract tests.

8. Apply integration tests in an isolated account or project when appropriate.

9. Review the plan through human approval for sensitive environments.

10. Apply, execute smoke tests, and record evidence.

11. Destroy ephemeral resources and publish test artifacts.

The plan is a particularly important test artifact. It communicates proposed additions, changes, and destructions, but it is not a complete guarantee of runtime success. Unknown values, provider behavior, permissions, eventual consistency, and external dependencies may only become visible during application. A quality process treats plan review as necessary but not sufficient.

Terraform state also affects testing. Shared state can cause tests to interfere with one another, while isolated state improves repeatability. Remote state access must be protected because it may contain sensitive values or resource identifiers. Test pipelines should use least-privilege credentials and avoid placing secrets in plans, logs, or assertion output.

Ansible Testing and Idempotence

Ansible’s declarative modules are designed to converge systems toward a desired state. Idempotence means that applying the same playbook to an already compliant host produces no unnecessary changes. It is a central reliability property because non-idempotent automation creates noise, extends deployment time, and may cause service disruption.

Ansible testing should examine syntax, lint quality, role behavior, idempotence, and failure handling. Molecule provides a lifecycle for dependency installation, instance creation, convergence, verification, and destruction. Testinfra or pytest can inspect packages, services, ports, files, permissions, users, and processes from an external perspective. Ansible’s check mode can provide useful previews, although not all modules support it perfectly.

A role should be tested on the operating systems and versions it claims to support. Differences in package managers, service names, filesystem paths, and security defaults can invalidate assumptions. Where containers do not reproduce systemd, networking, or kernel behavior adequately, virtual machines or dedicated test hosts are more appropriate.

Failure-path tests are often neglected. A reliable role should behave sensibly when a package repository is unavailable, a required variable is missing, a service fails to start, or a configuration template is invalid. Handlers, blocks, rescue sections, retries, and explicit assertions should be tested rather than assumed to work.

Ephemeral Environments and Test Data

An ephemeral environment is created for a test or short-lived validation period and then destroyed. It may be a temporary cloud account, project, namespace, virtual network, virtual machine set, or container environment. Ephemeral infrastructure improves isolation and reduces contamination from previous experiments. It also makes destructive testing more acceptable.

Effective ephemeral environments require automation for the entire lifecycle: creation, configuration, testing, collection of evidence, and destruction. Naming conventions and expiration tags support governance. Budget limits, quotas, and automatic cleanup jobs reduce financial risk. Credentials should be scoped to the test environment and revoked or rotated after use.

Ephemeral environments are not automatically representative. A small test account may have different quotas, network rules, identities, service limits, or data volumes from production. Teams should document which properties are faithfully reproduced and which are approximated. Production-like testing should avoid copying sensitive production data unless it has been anonymized and approved.

Test data must be deterministic where possible. Random identifiers can prevent collisions but should be recorded for diagnostics. Data generators should create realistic relationships without exposing personal or confidential information. Destruction tests should verify that temporary data and dependent resources are removed, not merely that the primary resource disappears.

Reliability Engineering: Failure, Recovery and Drift

Reliability testing asks how infrastructure behaves under adverse conditions. Relevant scenarios include a failed instance, unavailable dependency, expired credential, partial deployment, interrupted pipeline, corrupted configuration, and loss of a region or availability zone. The goal is not to create disorder for its own sake, but to validate documented assumptions about resilience and recovery.

Recovery objectives provide measurable targets. The recovery time objective specifies how quickly service should be restored; the recovery point objective specifies the acceptable amount of data loss. IaC supports recovery by making environments reproducible, but reproducibility depends on external state, backups, secrets, provider availability, and correct version pinning. A script that can recreate servers but cannot restore data is not a complete recovery strategy.

Drift occurs when actual infrastructure differs from the declared configuration. It can arise from emergency console changes, provider defaults, manual maintenance, or failed automation. Scheduled plan checks, configuration management scans, and cloud inventory comparisons can identify drift. Teams must decide whether to reconcile the environment, update the code, or formally accept the deviation. Automatic correction may be dangerous if the code is stale or if the manual change was an emergency safeguard.

Reliability evidence should include successful recovery exercises, not only design documents. A restore test, failover test, or rebuild from an empty account can reveal missing permissions, undocumented dependencies, and assumptions about ordering.

Applied Case Study: Testing a Multi-Environment Platform

Background

This illustrative case concerns Northstar Retail, an organization operating an online commerce platform. Its infrastructure was managed through Terraform modules for networking, Kubernetes, databases, identity, and observability. Ansible roles configured administrative hosts and selected supporting services. Development deployments were frequent, but production changes were associated with occasional outages and unexplained manual corrections.

Situation and Problem

The organization’s existing pipeline performed `terraform plan` and required an approval before apply. It did not enforce formatting, security policies, module contracts, or Ansible idempotence. A networking change once removed an expected route, while an Ansible role repeatedly restarted a service on every run. The incidents were not caused by one defective tool; they resulted from insufficient evidence at several levels.

Analysis

The platform team mapped failure modes to test layers. Syntax and style problems belonged in fast pre-commit checks. Security and regional restrictions belonged in policy tests. Module interface assumptions required contract tests. Provider interactions required integration tests. Customer-facing availability required end-to-end and smoke tests. Recovery claims required deliberate rebuild and restore exercises.

The team also identified governance risks. Shared test accounts made cleanup unreliable, credentials were broader than necessary, and plans sometimes exposed sensitive values in pipeline logs. Therefore, testing itself required security and lifecycle controls.

Approach and Solution

The team introduced a staged pipeline. Terraform formatting, validation, TFLint, and static security analysis ran on every change. A policy engine rejected unencrypted databases, unrestricted administrative ingress, and unapproved regions. Module tests asserted network outputs, database encryption settings, and variable validation. Contract tests ran whenever a shared module version changed.

Molecule scenarios tested Ansible roles on supported operating-system images. Verification used Testinfra to inspect services and permissions, and a second convergence confirmed idempotence. Integration tests used Terratest to create an isolated cloud environment, deploy the platform, verify private connectivity and database access, and destroy all resources.

A small end-to-end test sent authenticated requests through the load balancer and checked application health. A scheduled recovery exercise rebuilt a non-production environment from version-controlled code and restored a recent database backup. Test accounts used expiring credentials, resource tags, quotas, and automated cleanup.

Outcome

The new process detected invalid routes, missing encryption settings, unsupported operating-system assumptions, and non-idempotent tasks before production deployment. Pipeline duration increased for integration stages, so the team separated rapid pull-request checks from scheduled or release-level tests. The organization accepted that not every test belonged on every commit, but required traceable evidence before high-risk changes.

Learning Points

The case demonstrates that reliability is produced by a portfolio of controls rather than a single framework. Fast tests reduce feedback time; realistic tests reveal provider and system behavior; policy tests protect organizational boundaries; recovery tests validate operational claims. It also shows that testing strategy is a management decision involving cost, risk, speed, and evidence quality.

Challenges, Limitations and Common Mistakes

A common mistake is testing only whether a command exits successfully. Successful execution may conceal incorrect values, insecure exposure, missing dependencies, or a service that is technically running but unusable. Assertions should inspect meaningful outcomes.

Another mistake is overusing mocks. Mocks provide speed and isolation, but they can hide provider behavior, permission requirements, or eventual consistency. They should be balanced with a smaller number of real integration tests.

Flaky tests are especially harmful in infrastructure pipelines. They may result from eventual consistency, shared environments, timing assumptions, rate limits, or incomplete cleanup. Retries should be bounded and accompanied by diagnosis; indiscriminate retries can conceal real defects.

Teams also frequently test only the happy path. Invalid inputs, denied permissions, partial failure, rollback, interruption, and destruction deserve deliberate attention. Conversely, exhaustive end-to-end testing is often economically impractical. Risk-based prioritization is more effective than attempting to test every possible provider combination.

Version drift is another challenge. Provider releases, operating-system images, Ansible collections, Terraform versions, and cloud APIs change over time. Pinning versions improves reproducibility, while controlled upgrade tests prevent indefinite stagnation. Test dependencies should be updated deliberately rather than silently.

Governance, Ethics and Professional Responsibility

Infrastructure testing operates with powerful privileges. A test may create public endpoints, access data stores, alter identity permissions, or destroy resources. Least privilege, environment separation, approval controls, secret management, and audit logging are therefore professional obligations.

Security tests must avoid exposing real personal data. If production-like data is needed, it should be minimized, anonymized, or synthetically generated. Destructive tests require explicit authorization and clearly bounded targets. Teams should define ownership for test environments and incident response procedures for failed automation.

Quality gates should be transparent. A policy should not become an unexplained barrier that disproportionately affects certain teams or encourages bypasses. Exceptions must be governed, and test results should be retained sufficiently to support audit, incident analysis, and learning. The purpose of governance is not merely to prevent change; it is to make responsible change possible.

Emerging Practices and Best Practices

Modern pipelines increasingly treat plans, test reports, policy results, logs, and resource inventories as machine-readable evidence. This supports trend analysis, quality dashboards, and automated release decisions. Supply-chain security is also becoming more important: teams verify provider and collection sources, pin dependencies, scan modules, sign artifacts where appropriate, and review changes to reusable components.

Policy engines are being integrated earlier into developer workflows, while ephemeral preview environments allow reviewers to inspect functional changes before merge. Artificial intelligence may assist with test generation, anomaly detection, or plan explanation, but generated tests require expert review. AI systems can reproduce incorrect assumptions, mishandle secrets, or produce assertions that merely confirm implementation details.

Effective practice can be summarized as follows:

  • Test early, but include realistic provider-level tests for important behavior.
  • Treat plans as evidence requiring review, not as proof of correctness.
  • Test interfaces, idempotence, failure paths, and destruction as well as successful creation.
  • Isolate environments, credentials, state, and test data.
  • Make policies explainable and exceptions auditable.
  • Use risk to determine which tests run on each change.
  • Record artifacts, ownership, versions, and cleanup outcomes.
  • Test recovery in practice and revise documentation from the results.

Chapter Summary

Testing IaC is a layered quality and reliability discipline. Formatting and validation establish basic correctness; linting and static analysis identify maintainability and security risks; policy as code enforces organizational boundaries. Unit and module tests examine local decisions, contract tests protect interfaces, and integration tests verify behavior against real providers and operating systems. End-to-end and smoke tests establish confidence in complete workflows.

Terraform’s native testing capabilities, plan analysis, Terratest, and related tools support infrastructure verification. Ansible Lint, Molecule, Testinfra, pytest, and idempotence checks support role and playbook quality. Ephemeral environments improve isolation and realism when their credentials, costs, data, and destruction are governed properly. Reliability engineering extends testing to drift, failure, recovery, backups, and rebuilds.

The strongest strategy is risk-based and evidence-oriented. It balances fast feedback with realistic testing, avoids excessive reliance on mocks, controls flakiness, protects sensitive information, and treats recovery as a capability that must be demonstrated rather than assumed.

Key Terms

Infrastructure as Code (IaC): Management of infrastructure through version-controlled, machine-readable definitions.

Quality gate: An automated or human-controlled condition that must be satisfied before progression in a delivery process.

Idempotence: The property that repeated execution produces no unnecessary changes after the desired state is reached.

Static analysis: Examination of code or configuration without executing the complete infrastructure workflow.

Policy as code: Encoding organizational, security, compliance, or architectural rules in executable form.

Terraform plan: A representation of proposed infrastructure changes before application.

Module contract: The documented and testable interface of a reusable infrastructure component.

Contract test: A test that verifies assumptions between separately developed components.

Integration test: A test that evaluates interactions among components or with real external services.

End-to-end test: A test of a complete workflow from an entry point to its expected operational outcome.

Ephemeral environment: A temporary environment created for testing and removed afterward.

Drift: A difference between declared infrastructure configuration and actual deployed state.

Smoke test: A focused test of essential functionality after deployment or change.

Recovery time objective (RTO): The target time within which service should be restored.

Recovery point objective (RPO): The maximum acceptable amount of data loss measured in time.

Flaky test: A test whose result is inconsistent despite no relevant code change.

Molecule: A framework commonly used to develop and test Ansible roles.

Terratest: A Go-based testing framework frequently used to deploy and verify infrastructure.

Test artifact: A retained output, report, plan, log, or result that provides evidence of testing.

Review Questions

Short Answer Questions

1. Why is a successful Terraform apply insufficient evidence of infrastructure quality?

2. What is the difference between validation and linting in IaC?

3. Define idempotence and explain its importance in Ansible.

4. What purpose does a contract test serve in a Terraform module ecosystem?

5. Why should ephemeral environments use automatic cleanup and expiration controls?

Long Answer Questions

1. Explain the infrastructure testing pyramid and discuss the trade-off between speed, realism, and cost at different levels.

2. Describe a complete Terraform quality pipeline from formatting through post-deployment smoke testing.

3. Discuss how Molecule, Testinfra, and Ansible Lint can be combined to test an Ansible role.

4. Explain how policy as code contributes to security and governance, including the management of exceptions.

5. Analyze why recovery testing is a necessary part of reliability engineering for IaC-managed systems.

Analytical / Application Questions

1. A Terraform plan creates an encrypted database but also permits unrestricted administrative access from the internet. Identify which test layers should detect the problem and explain the appropriate quality gates.

2. An Ansible role reports changed status on every execution and occasionally restarts a production service. Design a testing and remediation approach.

3. Design an ephemeral integration environment for a multi-tier application. Specify isolation, credentials, test evidence, failure cleanup, and the limitations of its representativeness.

22. Multiple Choice Questions with Answers

Question 1

Which test layer is most appropriate for detecting that a Terraform module has changed the name and type of an output consumed by another team?

A. Formatting test

B. Contract test

C. End-to-end smoke test

D. Recovery test

Correct Answer: B. Contract test

Question 2

A Terraform configuration is syntactically valid but creates an unencrypted storage resource. Which control is most directly designed to reject it before deployment?

A. Policy as code

B. YAML formatting

C. Ansible convergence

D. DNS smoke testing

Correct Answer: A. Policy as code

Question 3

What does a second successful convergence of an Ansible role primarily demonstrate?

A. Provider version compatibility

B. Idempotence

C. Disaster recovery

D. Terraform state locking

Correct Answer: B. Idempotence

Question 4

Which statement best describes an ephemeral test environment?

A. A permanent production replica used for all releases

B. A temporary isolated environment created and destroyed through automation

C. A local text editor configuration

D. A provider policy that prevents resource deletion

Correct Answer: B. A temporary isolated environment created and destroyed through automation

Question 5

Why should integration tests supplement Terraform mocks?

A. Mocks always create real cloud resources

B. Integration tests reveal provider, permission, and runtime behavior that mocks may hide

C. Integration tests eliminate the need for policy checks

D. Mocks cannot test variable validation

Correct Answer: B. Integration tests reveal provider, permission, and runtime behavior that mocks may hide

Question 6

A pipeline retries a failing infrastructure test indefinitely until it passes. What is the principal quality risk?

A. The test becomes too deterministic

B. Retries may conceal genuine defects and increase resource cost

C. The infrastructure becomes automatically idempotent

D. The plan becomes easier to review

Correct Answer: B. Retries may conceal genuine defects and increase resource cost

Question 7

Which evidence most directly validates a documented recovery claim?

A. A successful formatting check

B. A code review approval

C. A practical rebuild and data-restore exercise

D. A lint warning count

Correct Answer: C. A practical rebuild and data-restore exercise

Question 8

What is the primary purpose of analyzing a Terraform plan in a delivery pipeline?

A. To guarantee that provider APIs cannot fail

B. To inspect intended additions, changes, and destructions before application

C. To replace all runtime testing

D. To remove the need for state management

Correct Answer: B. To inspect intended additions, changes, and destructions before application

Question 9

Which practice best protects sensitive information during infrastructure testing?

A. Copying production data into every test account

B. Printing all variables in pipeline logs

C. Using least-privilege credentials and sanitized or synthetic test data

D. Disabling audit logging

Correct Answer: C. Using least-privilege credentials and sanitized or synthetic test data

Question 10

An organization wants rapid pull-request feedback but also needs realistic cloud validation. Which strategy is most appropriate?

A. Run only end-to-end tests on every commit

B. Use fast validation and policy checks for every change, with risk-based integration tests at suitable stages

C. Remove all automated tests and rely on approval

D. Use only mocked tests for production changes

Correct Answer: B. Use fast validation and policy checks for every change, with risk-based integration tests at suitable stages

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 10 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 11
Security, Secrets and Policy as Code

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

11. Security, Secrets and Policy as Code

Introduction and Context

Infrastructure as Code (IaC) changes infrastructure from a manually operated environment into a system described, reviewed, tested, and executed through machine-readable definitions. This improves consistency and repeatability, but it also changes the nature of security risk. A password placed in a Terraform variable, an overly broad cloud role, an unverified Ansible collection, or an unprotected state file can expose an entire organization even when the infrastructure itself appears correctly configured.

Security in IaC is therefore not a final inspection activity. It is a property of the complete delivery system: source repositories, developer workstations, CI/CD pipelines, state storage, cloud identities, providers, modules, collections, policies, and runtime infrastructure. The central objective is to make the secure path the normal path while preserving sufficient flexibility for legitimate engineering work.

This chapter examines least privilege, secrets management, state security, encryption, software supply-chain controls, provenance, policy as code, compliance automation, vulnerability scanning, and secure coding practices. Terraform and Ansible are used as representative technologies, but the principles apply to infrastructure automation generally.

Learning Objectives

After studying this chapter, the learner should be able to:

  • distinguish infrastructure secrets, credentials, sensitive data, and configuration;
  • design least-privilege identities for Terraform and Ansible automation;
  • secure Terraform state and understand why marking a value as sensitive is not encryption;
  • select appropriate secret-management and encryption approaches;
  • evaluate providers, modules, roles, and collections for provenance and supply-chain risk;
  • apply policy as code, static analysis, vulnerability scanning, and compliance automation;
  • construct a secure IaC workflow that prevents accidental disclosure;
  • analyse governance, ethical, regulatory, and operational implications of IaC security decisions.

Security as a Property of the IaC System

A useful security model considers four related dimensions. Confidentiality prevents unauthorized disclosure of credentials, keys, personal data, and state. Integrity ensures that infrastructure definitions, policies, providers, and execution plans have not been altered improperly. Availability protects the ability to provision, recover, and manage infrastructure. Accountability makes actions attributable through identity, approvals, logs, and version history.

These dimensions interact. Encrypting a state file protects confidentiality, but does not prevent an administrator with write access from changing it. A signed module improves integrity, but does not guarantee that the module’s author followed secure coding practices. A highly restrictive policy may improve security but damage availability if it blocks emergency recovery. Professional design therefore balances control, operational resilience, and justified business requirements.

The principle of least privilege is the foundation. An identity should have only the permissions needed for a defined task, for the shortest practical duration, and within the narrowest applicable scope. Least privilege applies to cloud roles, CI runners, state backends, repository users, secret engines, Ansible become privileges, and human break-glass access. It is not equivalent to giving an automation account administrator access because administration is convenient.

A mature implementation separates identities by function. Planning may use read-only permissions, whereas applying changes requires narrowly scoped write permissions. Development, testing, and production should not share credentials. A CI job that deploys a network should not automatically be able to read database passwords. Where supported, short-lived federated credentials are preferable to long-lived static access keys.

Secrets, Sensitive Values, and State

A secret is information whose disclosure could enable unauthorized access or cause harm. Examples include passwords, API tokens, private keys, database connection strings, signing keys, and bootstrap credentials. Sensitive business data may not be a credential but still requires protection. A value marked `sensitive` in Terraform is commonly hidden from ordinary CLI output; it is not thereby removed from the plan or encrypted in state.

Terraform state records the relationship between configuration and real infrastructure. It may contain resource attributes, generated passwords, endpoint details, and values that are not obvious in configuration files. Consequently, state must be treated as confidential and authoritative operational data. Recommended controls include:

  • use a remote backend with encryption at rest and in transit;
  • restrict read, write, and administrative permissions separately;
  • enable versioning and carefully controlled recovery;
  • use locking to reduce concurrent modification and corruption;
  • audit access to the backend;
  • avoid storing state in unprotected local workstations;
  • limit state contents by separating environments or components when appropriate;
  • prevent state files, plans, and debug logs from entering source control or build artifacts.

A plan file can also contain sensitive values. It must receive controls comparable to state. Log masking is useful but imperfect: secrets can appear through error messages, shell tracing, provider diagnostics, or encoded output. Secure pipelines should disable command tracing for secret-bearing commands, use ephemeral workspaces, and define retention periods for logs and artifacts.

Ansible introduces related risks through variables, inventory files, fact gathering, registered task output, and become credentials. `no_log: true` can suppress task output, but it should be used carefully because excessive suppression reduces auditability and troubleshooting capability. Secrets should be retrieved at execution time from a managed secret store rather than committed to inventories or variable files. Ansible Vault can protect encrypted files, but its key-management process, rotation, access control, and CI integration remain organizational responsibilities.

Secret Management and Encryption Architecture

A secret-management system should provide controlled storage, identity-based retrieval, rotation, auditing, and—where practical—dynamic credentials. Examples include cloud secret managers, enterprise vault platforms, and dedicated key-management services. The automation tool should receive only the secret required for the current operation. It should not export an entire secret database to a job.

Secret retrieval can follow a pull model, in which a trusted workload authenticates to a secret service, or a brokered model, in which a CI platform injects a temporary value. In both cases, authentication must be stronger than possession of a repository file. Workload identity federation, short-lived tokens, and environment-specific authorization reduce the impact of compromise.

Encryption has three distinct uses. Encryption in transit protects communication between clients, backends, secret stores, and target systems. Encryption at rest protects persisted data such as state, backups, and logs. Application-level or field-level encryption protects especially sensitive values even when storage systems are accessed. Key management must address generation, storage, rotation, access, separation of duties, backup, revocation, and destruction. Encryption is not a substitute for access control: an authorized process with a decryption capability can still disclose data.

A sound design also distinguishes encryption keys from data-encryption keys and records which identities may use each key. Key policies should be reviewed like source code. Emergency access must be logged and periodically examined.

Supply-Chain Security and Provenance

IaC depends on external components: Terraform providers, modules, Ansible roles, collections, container images, plugins, and action scripts. These dependencies create a software supply-chain risk. A compromised or malicious component may execute code, exfiltrate credentials, alter resources, or introduce a backdoor into every environment that consumes it.

Provenance is evidence about where an artifact came from, how it was built, and whether it is the expected artifact. Useful controls include trusted registries, provider and collection signatures where supported, checksum verification, version pinning, dependency lock files, internal mirrors, software bills of materials, and review of release ownership. Pinning a version improves reproducibility, but a pinned malicious version remains malicious; selection and provenance are both necessary.

Modules and roles should be treated as software products. They require documentation, tests, change review, security scanning, ownership, and a deprecation process. Organizations should prefer minimal dependencies and avoid copying opaque code into production pipelines. A private registry or curated collection repository can reduce uncontrolled external consumption, but it must itself be secured and maintained.

A secure pipeline separates dependency acquisition from execution. It can verify signatures and checksums before downloading, scan the selected versions, and prevent unapproved sources. Credentials should not be available while untrusted dependency-installation scripts run. This is especially important when tools or plugins can execute local code during initialization or deployment.

Policy as Code and Compliance Automation

Policy as code expresses organizational, security, or regulatory rules in machine-evaluable form. Instead of relying solely on a document stating that storage must be encrypted, a policy can reject a Terraform plan that creates an unencrypted storage resource. Policy therefore converts intent into an enforceable control.

Policies may be applied at several points:

1. Source stage: detect insecure patterns before review.

2. Plan stage: evaluate the proposed infrastructure before changes occur.

3. Admission stage: prevent unauthorized deployment.

4. Runtime stage: identify drift or non-compliance in deployed resources.

5. Evidence stage: retain decisions, exceptions, and remediation records.

A policy should identify the rule, scope, rationale, severity, decision, and exception process. Examples include requiring encryption, prohibiting public administrative ports, restricting regions, requiring approved tags, preventing unrestricted identity permissions, and requiring backup or logging controls.

Policies can be implemented through Terraform validation rules, Sentinel or Open Policy Agent-style engines, Rego policies, cloud-native controls, Ansible lint rules, and CI checks. Different mechanisms have different visibility and enforcement points. A variable validation rule may ensure a value is well formed, while a plan policy can assess relationships among multiple resources.

Effective compliance automation is risk-based. A blocking rule is appropriate for a high-impact violation, whereas a warning may be suitable for a low-risk development exception. Exceptions must be explicit, time-bound, approved by an accountable owner, and periodically reviewed. Otherwise, exceptions become an ungoverned alternative deployment path.

Secure IaC Coding and Verification Workflow

Secure coding begins with safe defaults. Modules should create private resources unless public exposure is explicitly requested. Variables should have type constraints and validation. Resource names, regions, and environment identifiers should be restricted to approved values. Dynamic construction of policy documents, shell commands, or templates should be handled cautiously to avoid injection and unintended privilege.

Terraform configurations benefit from formatting, validation, provider locking, static analysis, secret scanning, and plan review. Tools such as Terraform-native validation, linters, security scanners, and policy engines can identify insecure configurations, but no scanner understands every organizational context. Ansible projects benefit from syntax checking, linting, molecule-style testing where appropriate, inventory validation, safe handling of privilege escalation, and review of shell or command tasks.

A representative pipeline may contain these stages:

  • authenticate using a short-lived workload identity;
  • fetch approved dependencies and verify their versions and provenance;
  • scan source for secrets and unsafe patterns;
  • format and syntactically validate configuration;
  • run unit or module tests and static security analysis;
  • generate a plan without exposing it in public logs;
  • evaluate policy and compliance controls;
  • obtain peer approval for sensitive environments;
  • apply through a controlled runner;
  • record audit evidence and perform post-deployment checks.

Vulnerability scanning should cover both IaC definitions and the systems they create. IaC scanning may detect public storage or weak security groups; image and package scanning may identify vulnerable operating-system components; runtime tools may detect drift or exposed services. Findings must be prioritized by exploitability, exposure, asset value, and compensating controls rather than by scanner count alone.

A Strategic Control Framework

A practical governance framework can be organized into five control layers. Identity controls govern who or what may execute actions. Content controls govern the safety and provenance of code and dependencies. Execution controls govern where and how plans are applied. Data controls protect state, secrets, logs, and backups. Assurance controls test, monitor, document, and improve the other layers.

This model supports separation of duties. One team may maintain reusable modules, another may approve production policies, and a platform team may operate the execution environment. Excessive separation can create delays and encourage bypasses, so responsibilities should be explicit rather than needlessly fragmented. Managers must also fund remediation capacity; a policy that continuously produces unaddressed findings is not effective governance.

Illustrative Case Study: Securing a Multi-Environment Delivery Platform

Background

A fictional financial-services organization, Northstar Finance, used Terraform for cloud networks and managed services and Ansible for operating-system configuration. Development teams stored configurations in a central Git repository, while a CI platform performed deployments to development, staging, and production.

Situation and Problem

An internal review found three weaknesses. A cloud access key was stored in a CI variable with a long lifetime. Terraform state was held in a shared object-storage location with broad read access. Several teams downloaded public modules without version constraints. No policy check blocked public storage or unrestricted administrative ingress.

The organization had not experienced a confirmed breach, but the combination created a high-impact risk. A compromised repository account could expose credentials and state, modify dependencies, and deploy resources with excessive permissions.

Analysis

The review classified the risks by confidentiality, integrity, and blast radius. The long-lived key could be reused outside CI. Shared state access exposed secrets and enabled unauthorized changes. Uncontrolled modules created a supply-chain dependency without reliable provenance. The absence of policy enforcement allowed insecure designs to pass ordinary code review.

Approach and Solution

Northstar replaced static CI keys with workload identity federation and separate roles for planning and applying. Production apply permissions were restricted to a dedicated runner and required approval. State was moved to an encrypted remote backend with locking, versioning, access logs, and separate state boundaries for environments.

The platform team established a private module registry and required provider lock files and approved versions. Dependencies were reviewed, scanned, and periodically updated. A secret manager replaced repository and CI-file storage of credentials; jobs received short-lived, narrowly scoped values.

Policy checks were added to reject public storage, unrestricted administrative ports, unencrypted databases, missing ownership tags, and deployment to unapproved regions. A documented exception process allowed temporary deviations with an owner and expiry date. Secret scanning, IaC scanning, Ansible linting, and plan review became mandatory pipeline stages.

Outcome

The organization reduced the persistence and scope of deployment credentials, improved visibility into state access, and prevented several insecure designs before deployment. The process initially increased pipeline duration and required module remediation. Management addressed this by providing reusable secure modules, clear policy guidance, and a service-level target for exception review.

Learning Points

The case demonstrates that IaC security is systemic. No single tool solved the problem. Identity, state protection, provenance, policy, testing, and governance had to operate together. It also shows that enforcement is more successful when teams receive secure abstractions and practical remediation support rather than only rejection messages.

Challenges, Limitations, and Common Mistakes

A common mistake is assuming that a hidden console value is a protected secret. Sensitive flags, masking, and encrypted files reduce exposure but do not eliminate it. Another mistake is granting the entire deployment system administrator privileges because resource dependencies are not understood. Permissions should be iteratively narrowed using observed use, documentation, and separate roles.

Organizations also overestimate scanners. False positives produce alert fatigue, while false negatives occur when a tool lacks context or cannot evaluate runtime behavior. Scanning should be combined with architecture review, threat modelling, testing, and monitoring. Excessive policy strictness can encourage manual changes outside IaC, creating drift and reducing accountability.

Emergency access presents another challenge. A fully blocked production environment may be unsafe during a major incident. Break-glass access should therefore exist where justified, use strong authentication, be time limited, generate high-priority alerts, and require retrospective review. Finally, security controls themselves require maintenance: providers change, cloud services evolve, policies become obsolete, and cryptographic practices require periodic reassessment.

Ethical, Regulatory, and Professional Considerations

IaC decisions affect employees, customers, and the public. Exposing personal data through an incorrectly configured resource can cause harm beyond technical inconvenience. Professionals must apply data minimization, purpose limitation, appropriate retention, and access accountability. Regulatory requirements may demand evidence of access control, encryption, change management, logging, or recovery capability, but compliance should not be reduced to checkbox activity.

Security exceptions require ethical judgment. A business deadline does not automatically justify accepting an unbounded risk. Decision-makers should document the affected assets, probability, impact, compensating controls, owner, and expiration. Engineers also have a professional responsibility not to conceal insecure defaults or suppress material findings merely to achieve deployment targets.

Emerging Directions

Current practice is moving toward identity-centric automation, policy-driven platforms, signed artifacts, continuous compliance, and automated drift remediation. Software supply-chain frameworks increasingly emphasize verifiable build processes and attestations. Machine learning may assist in detecting anomalous changes or prioritizing findings, but automated recommendations require human validation because infrastructure context and business impact are difficult to infer reliably.

The strategic direction is toward secure-by-default platforms: approved modules, centrally managed identities, standardized pipelines, policy libraries, and observable execution environments. Such platforms shift security from individual expertise toward organizational capability while preserving appropriate team autonomy.

Best Practices

  • Use short-lived, workload-specific identities rather than shared static credentials.
  • Protect state, plans, logs, backups, and artifacts as sensitive data.
  • Retrieve secrets at runtime from an audited secret-management system.
  • Encrypt data in transit and at rest, and manage keys separately from protected data.
  • Pin and verify providers, modules, roles, collections, and other dependencies.
  • Maintain a curated registry and document component provenance.
  • Scan source, dependencies, images, plans, and runtime environments.
  • Enforce high-risk controls through policy as code before deployment.
  • Separate planning, approval, and production application responsibilities.
  • Test secure modules and provide safe defaults.
  • Make exceptions explicit, time limited, and auditable.
  • Monitor state access, privileged actions, drift, and emergency use.

Chapter Summary

Security in Terraform and Ansible is a system property covering identities, code, dependencies, execution, state, secrets, and operational evidence. Least privilege limits the impact of compromised accounts, while secure state backends protect the sensitive record of infrastructure. Secret managers, encryption, key governance, and short-lived credentials reduce exposure. Provider, module, role, and collection provenance controls address supply-chain risk. Policy as code converts security and compliance requirements into enforceable decisions across source, plan, admission, and runtime stages. Scanning and testing are valuable but must be combined with contextual review, monitoring, and governance. Effective organizations make secure defaults reusable, exceptions accountable, and automation auditable.

Key Terms

Least privilege: Granting only the permissions necessary for a task, identity, and time period.

Secret: Sensitive information whose disclosure may enable unauthorized access or cause harm.

Terraform state: Data recording Terraform’s knowledge of managed resources and their attributes.

Sensitive value: A value treated specially to reduce display or logging exposure; it is not automatically encrypted.

Remote backend: A service or location used to store and coordinate Terraform state outside a local workstation.

State locking: A mechanism that prevents conflicting concurrent changes to state.

Workload identity: A machine identity used by an application, runner, or automation process.

Federated identity: An identity established through a trusted relationship without storing a long-lived local credential.

Encryption at rest: Protection applied to persisted data.

Encryption in transit: Protection applied to data moving between systems.

Key management: The governance of cryptographic-key creation, use, rotation, storage, and revocation.

Provenance: Evidence describing an artifact’s origin, construction, and integrity.

Dependency pinning: Restricting a dependency to a specified version or approved range.

Policy as code: Machine-evaluable rules that express and enforce organizational requirements.

Compliance automation: Automated assessment, prevention, or evidence collection for required controls.

Drift: A difference between declared infrastructure and the actual deployed environment.

Break-glass access: Exceptional emergency access subject to heightened controls and review.

Supply-chain risk: Risk introduced through external components, tools, build systems, or dependencies.

Review Questions

Short Answer Questions

1. Why is Terraform state considered sensitive even when configuration files contain no passwords?

2. Distinguish a sensitive value from an encrypted value.

3. What is the purpose of provider or collection provenance?

4. How does workload identity improve on long-lived access keys?

5. What characteristics should a valid policy exception have?

Long Answer Questions

1. Explain how least privilege should be applied across developers, CI runners, Terraform backends, and Ansible execution.

2. Discuss a complete architecture for managing secrets and encryption in an IaC platform.

3. Explain the role of policy as code across source, plan, admission, runtime, and evidence stages.

4. Analyse the major supply-chain risks associated with Terraform providers and Ansible collections and propose controls.

5. Design a secure IaC pipeline for a production environment, including testing, scanning, approval, deployment, and monitoring.

Analytical / Application Questions

1. A CI job can read all production secrets and apply any Terraform resource. Analyse the risks and redesign its identity and execution model.

2. A policy blocks every public endpoint, but a documented business service requires one. Develop an exception and compensating-control process that preserves governance.

3. An organization uses encrypted state but stores plan files and verbose logs indefinitely in a shared artifact repository. Evaluate the weakness and recommend corrective measures.

22. Multiple Choice Questions with Answers

Question 1

Which statement most accurately describes a Terraform sensitive value?

A. It is always encrypted in the state file.

B. It is hidden from all users, including authorized administrators.

C. It reduces ordinary display exposure but may still exist in state or plans.

D. It automatically retrieves the value from a secret manager.

Correct Answer: C. It reduces ordinary display exposure but may still exist in state or plans.

Question 2

Which design best demonstrates least privilege in a CI/CD system?

A. One administrator credential is shared by all pipelines.

B. Planning and production application use separate, narrowly scoped identities.

C. Every pipeline receives permission to read all organizational secrets.

D. Developers use the production deployment identity for troubleshooting.

Correct Answer: B. Planning and production application use separate, narrowly scoped identities.

Question 3

Why must Terraform plan files be protected?

A. They are required only for formatting configuration.

B. They can contain proposed resource attributes and sensitive values.

C. They cannot be regenerated after a failed deployment.

D. They replace the need for state locking.

Correct Answer: B. They can contain proposed resource attributes and sensitive values.

Question 4

Which control most directly addresses the provenance of an external Terraform provider?

A. Increasing the CI runner’s memory.

B. Disabling state versioning.

C. Verifying approved versions, checksums, or signatures.

D. Allowing unrestricted provider downloads.

Correct Answer: C. Verifying approved versions, checksums, or signatures.

Question 5

At which stage does a policy that rejects an unencrypted database in a Terraform plan operate most directly?

A. Plan evaluation before application.

B. Employee performance review.

C. Cryptographic key destruction only.

D. Physical network cabling.

Correct Answer: A. Plan evaluation before application.

Question 6

What is the primary advantage of short-lived federated credentials?

A. They eliminate the need for authorization.

B. They reduce the persistence and reuse of stolen credentials.

C. They allow every job to become an administrator.

D. They prevent all configuration errors.

Correct Answer: B. They reduce the persistence and reuse of stolen credentials.

Question 7

An Ansible task uses `no_log: true` for every task in a playbook. What is the main concern?

A. It necessarily encrypts all inventory files.

B. It may reduce auditability and make troubleshooting difficult.

C. It grants excessive cloud permissions.

D. It verifies collection signatures.

Correct Answer: B. It may reduce auditability and make troubleshooting difficult.

Question 8

Which response is most appropriate for a high-impact policy exception?

A. Ignore the violation if deployment is urgent.

B. Make the exception permanent and undocumented.

C. Record an owner, rationale, expiry, approval, and compensating controls.

D. Move the deployment outside the IaC pipeline.

Correct Answer: C. Record an owner, rationale, expiry, approval, and compensating controls.

Question 9

Why are security scanners insufficient as the only IaC security control?

A. They cannot identify any syntax errors.

B. They always block every deployment.

C. They may lack business context and produce false positives or negatives.

D. They make state storage unnecessary.

Correct Answer: C. They may lack business context and produce false positives or negatives.

Question 10

Which control most directly reduces concurrent Terraform state corruption?

A. State locking.

B. Public state access.

C. Unpinned modules.

D. Disabling audit logs.

Correct Answer: A. State locking.

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 11 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 12
CI/CD, GitOps and Platform Engineering

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

12. CI/CD, GitOps and Platform Engineering

Introduction and Context

Infrastructure as Code (IaC) becomes strategically valuable when it is connected to the wider software delivery system. Writing Terraform modules or Ansible playbooks is only one part of infrastructure automation. The more difficult organizational problem is deciding how infrastructure changes are proposed, reviewed, tested, approved, deployed, observed, and, when necessary, reversed. These activities form an infrastructure delivery lifecycle.

Continuous integration and continuous delivery (CI/CD) provide the operational workflow for that lifecycle. GitOps extends the model by treating version-controlled repositories as the authoritative source of desired system state. Platform engineering then builds reusable internal products—such as deployment templates, infrastructure modules, policy controls, and self-service portals—that allow development teams to consume infrastructure safely without mastering every underlying implementation detail.

Together, these practices change infrastructure from an individually operated technical resource into a governed, collaborative, and programmable organizational capability. They also introduce important questions. Who may approve a production change? How can a team prove what was deployed and why? When should automation stop and require human judgment? How can developers obtain databases, networks, or environments without bypassing security and cost controls? This chapter examines these questions through the connected perspectives of CI/CD, GitOps, platform engineering, self-service infrastructure, golden paths, and progressive delivery.

Learning Objectives

By the end of this chapter, the reader should be able to:

1. Explain how CI/CD pipelines apply to Terraform and Ansible workflows.

2. Design a controlled sequence of validation, planning, review, approval, application, rollback, and audit stages.

3. Distinguish push-based IaC delivery from pull-based GitOps.

4. Evaluate the role of repositories, state, policy, credentials, and observability in infrastructure delivery.

5. Explain how internal developer platforms, self-service infrastructure, and golden paths support organizational scalability.

6. Relate progressive delivery to infrastructure and application change management.

7. Analyze the risks, governance requirements, and trade-offs of automated infrastructure pipelines.

From IaC Code to a Governed Delivery System

An IaC repository contains more than executable configuration. It represents an operational contract: the desired resources, their relationships, security properties, dependencies, and ownership. A reliable delivery system must therefore establish confidence before changing a live environment.

A typical infrastructure change moves through several logical states. A developer first modifies a Terraform module, Terraform root configuration, Ansible role, inventory definition, or policy file. Automated checks then inspect syntax, formatting, static correctness, security implications, and compliance rules. Terraform may generate a plan showing the proposed resource actions, while Ansible may perform a dry run using check mode. Reviewers examine both the code and the expected operational effect. After authorization, a controlled execution mechanism applies the change. The resulting state is recorded through version history, pipeline logs, cloud audit trails, and monitoring systems.

This sequence is more significant than the particular CI product used. GitHub Actions, GitLab CI, Jenkins, Azure DevOps, and other systems can all implement comparable principles. The essential design question is whether the pipeline makes change visible, repeatable, attributable, and reversible.

A useful distinction is between continuous integration and continuous delivery. Continuous integration validates proposed changes frequently and early. In IaC, this may include formatting checks, module tests, validation commands, dependency analysis, policy tests, and security scanning. Continuous delivery ensures that a validated change can be released through an established path, usually with an approval boundary for sensitive environments. Continuous deployment goes further by automatically releasing changes when predefined conditions are met. Production infrastructure does not necessarily require continuous deployment; an organization may rationally choose continuous delivery with explicit human authorization.

A Reference IaC Pipeline

Although implementations vary, a mature pipeline commonly contains the following stages.

Source, Branching, and Change Intent

The workflow begins with a version-controlled change. A pull request or merge request should communicate not only what files changed, but also why the change is needed, which environments are affected, expected cost or capacity implications, and any migration or rollback considerations. Small, focused changes are easier to review and reverse than large collections of unrelated modifications.

Branching policies should protect important branches, require successful checks, and prevent unreviewed direct changes. Some organizations use short-lived branches; others use trunk-based development. The choice is less important than maintaining a clear relationship between an approved commit and the deployed result.

Validation and Testing

Validation operates at several levels. Basic checks include formatting, syntax, type or schema validation, and dependency resolution. Terraform commonly uses `terraform fmt`, `terraform validate`, and provider or module tests. Ansible workflows may use YAML validation, `ansible-lint`, role tests, and Molecule-based testing.

More advanced tests examine behavior. A Terraform module might be tested against an ephemeral account or environment, checking that a network, identity policy, or database is created with the intended properties. Ansible tests may verify idempotence: running the playbook a second time should produce no unintended changes. Security scanners inspect exposed ports, weak encryption settings, embedded credentials, and excessive permissions. Policy-as-code tools can reject configurations that violate organizational rules before they reach a cloud account.

Testing is not equivalent to proving that a change is safe in every circumstance. Infrastructure interacts with external systems, quotas, provider behavior, existing state, and data. Tests reduce uncertainty; they do not eliminate the need for review and operational judgment.

Plan and Preview

The plan stage is the central visibility mechanism in Terraform-based delivery. `terraform plan` compares configuration with the current state and provider observations, producing proposed additions, modifications, and destructions. A plan should be generated from the same commit that reviewers approve and should be retained as an artifact where practical.

Ansible does not produce an identical dependency graph, but check mode, diff mode, and controlled inventory execution provide a preview of intended actions. The quality of this preview depends on module behavior, fact gathering, privilege configuration, and whether tasks accurately report changes.

A plan is evidence, not authorization. Reviewers should examine destructive actions, identity changes, network exposure, replacement of stateful resources, unexpected drift, and changes in cost or availability. A technically valid plan may still be operationally inappropriate.

Review and Approval

Code review provides distributed technical judgment. Infrastructure review should include appropriate expertise for the affected domain: application engineering, security, networking, data protection, or finance where relevant. A change affecting a production database should not be approved solely because the syntax is valid.

Approval models should be risk-based. Low-risk changes may be automatically merged after checks. High-risk changes may require environment owners, separation of duties, scheduled maintenance windows, or explicit business authorization. Approval should be linked to a specific commit and plan rather than to an informal message or a mutable branch.

Apply and Release

The apply stage changes the target environment. It should use controlled credentials, a locked state backend, concurrency protection, and a known artifact. Terraform pipelines should avoid generating a new plan after approval unless the regenerated plan is reviewed again; applying a materially different plan undermines the approval process.

For Ansible, the pipeline should pin the collection and role versions, identify the inventory and variables used, and record the exact command context. Production execution should be isolated from developer workstations. Pipeline agents require only the permissions necessary for their task, and short-lived credentials are preferable to long-lived static secrets.

Verification, Rollback, and Audit

Deployment is incomplete until the outcome is verified. Verification may include cloud resource queries, service health checks, synthetic transactions, policy evaluation, configuration compliance, and application-level metrics. A successful command does not guarantee a healthy service.

Rollback requires careful definition. For stateless infrastructure, reverting a commit and applying it may restore a previous configuration. For database schemas, data migrations, security policy changes, or irreversible resource replacement, rollback may be impossible or unsafe. A forward fix, backup restoration, traffic diversion, or blue-green switch may be more appropriate. Rollback plans must therefore be designed before deployment, not invented during an incident.

Auditability connects technical execution with governance. A strong record identifies the requester, commit, reviewer, plan, approver, executor, target environment, credentials or identity used, timestamps, resulting changes, and verification evidence. Repository history alone is insufficient because it does not prove what was actually applied.

GitOps: Desired State and Reconciliation

GitOps applies a particular operating model to delivery. Desired state is stored declaratively in a version-controlled repository, and an automated agent continuously or periodically compares that desired state with the actual environment. When a difference exists, the agent attempts to reconcile the environment.

This model is especially prominent in Kubernetes, where controllers continuously reconcile resources, but its principles can also be applied to cloud infrastructure and configuration management. A GitOps controller may invoke Terraform, manage Terraform-compatible resources, or coordinate other declarative operators. The key feature is not simply that Git is used; it is that the repository is treated as the authoritative declaration and the deployment mechanism observes and reconciles from it.

GitOps is often contrasted with a conventional push pipeline. In a push model, a CI runner receives a commit and actively connects to the target environment to apply it. In a pull model, an agent inside or near the target environment retrieves approved desired state and performs reconciliation. Pull-based delivery can reduce inbound network access and keep deployment credentials within the environment. It can also improve consistency across many clusters or accounts.

GitOps introduces its own design issues. If an administrator changes a resource manually, the controller may revert that change, creating a conflict between emergency action and declared state. If the repository contains a faulty declaration, the controller may repeatedly apply an undesirable state. Reconciliation must therefore be combined with validation, policy controls, drift alerts, emergency procedures, and clear ownership.

A practical pattern separates application source from environment configuration. An application pipeline builds and signs an image, while a configuration repository records the approved image version and deployment parameters. A pull request changes the environment repository, and the GitOps agent reconciles the approved result. This separation improves traceability, but it also adds coordination between repositories and may increase release latency if poorly designed.

Platform Engineering and the Internal Developer Platform

Platform engineering responds to a recurring organizational problem: teams need infrastructure capabilities, but asking every team to become expert in cloud networking, identity, Terraform internals, security policy, and operational reliability is inefficient and unsafe. A platform team creates an internal developer platform (IDP) that packages these capabilities behind consistent interfaces.

An IDP is not merely a portal. It may include service catalogs, infrastructure modules, templates, deployment workflows, identity integrations, observability defaults, policy enforcement, documentation, and support processes. Its purpose is to reduce cognitive load while preserving appropriate control. Developers should be able to request a service or environment through a well-defined interface without needing to understand every implementation detail, but they should still understand the resulting ownership, cost, data classification, and operational responsibilities.

The platform should be treated as a product. It has users, user journeys, reliability expectations, documentation, versioning, feedback mechanisms, and a lifecycle. Platform teams must avoid becoming a centralized ticket queue. Self-service is valuable when it provides a safe, repeatable path with rapid feedback; it is not achieved merely by placing a request form in front of a manual approval process.

Self-Service Infrastructure

Self-service infrastructure allows an authorized user or team to provision approved capabilities independently. Examples include creating a development environment, requesting a managed database, registering a message queue, or deploying a standard web service. Underlying Terraform modules and Ansible roles encode organizational knowledge, while interfaces expose only supported choices and parameters.

Effective self-service requires guardrails. Parameters may constrain regions, instance sizes, network zones, encryption settings, retention periods, and allowed images. Automated policy can enforce mandatory tags, ownership metadata, budget limits, vulnerability standards, and data residency requirements. The platform should also provide lifecycle operations such as upgrade, backup, decommissioning, and ownership transfer; provisioning alone creates unmanaged sprawl.

Golden Paths

A golden path is a recommended, supported route for performing a common task. It is not an inflexible rule and should not prevent justified exceptions. A golden path for a web service might provide a repository template, CI workflow, container build, deployment configuration, standard monitoring, identity integration, and infrastructure provisioning. The user receives a productive default while the organization obtains consistency in security, reliability, and observability.

Golden paths succeed when they are easier than bypassing them. Excessive abstraction, slow provisioning, poor documentation, or lack of customization drives teams toward unapproved alternatives. Platform teams should therefore measure adoption, time to first deployment, failure rates, support demand, and user satisfaction rather than assuming that publication equals value.

Progressive Delivery and Risk Reduction

Progressive delivery releases change gradually and uses feedback to determine whether to continue. It applies to application versions, infrastructure changes, configuration, and policy updates. Common techniques include canary releases, blue-green deployment, feature flags, ring-based rollout, staged regional deployment, and automated rollback.

For an application, a canary may send a small percentage of traffic to a new version while monitoring latency, errors, saturation, and business indicators. For infrastructure, progressive delivery may involve applying a module change to a development account, then a staging environment, then one production region before wider rollout. Changes to a fleet can be partitioned by host groups or availability zones using Ansible.

Progressive delivery depends on reliable signals and explicit decision criteria. “No pipeline error” is not a sufficient health signal. Teams should define thresholds, observation periods, abort conditions, and ownership of the decision. Automation can pause or reverse a release, but it cannot compensate for missing telemetry or poorly chosen indicators.

Infrastructure changes sometimes cannot be progressively rolled out. A global IAM policy, central network route, or shared control-plane upgrade may affect many consumers simultaneously. In such cases, techniques such as compatibility windows, staged policy enforcement, shadow evaluation, backups, and tested recovery procedures become more important.

An Operating Model for Governance and Accountability

Automation changes the distribution of responsibility; it does not remove responsibility. A mature organization defines who owns modules, environments, pipelines, policies, state, secrets, and operational outcomes. The RACI model—responsible, accountable, consulted, and informed—can clarify roles, although it should not become a bureaucratic substitute for clear engineering ownership.

Policy should be implemented at multiple layers. Repository controls protect branches and require reviews. CI checks validate code and dependencies. Policy-as-code evaluates infrastructure intent. Cloud organization policies constrain what can be created. Runtime monitoring detects drift, vulnerabilities, and service degradation. No single control is sufficient because each observes a different stage of the lifecycle.

Secrets require particular care. Credentials should not be committed to repositories, printed in plans or logs, or embedded in machine images. Secret managers, workload identity, short-lived tokens, masked variables, and least-privilege roles reduce exposure. Terraform state may contain sensitive values even when configuration does not; encrypted remote backends, access controls, state locking, and appropriate redaction are therefore essential.

Compliance evidence should be produced as a by-product of delivery rather than assembled manually after the fact. Signed commits, immutable artifacts, plan records, approval logs, policy results, cloud audit events, and monitoring evidence can establish an auditable chain. Nevertheless, automation must respect privacy and employment considerations: excessive individual surveillance of pipeline activity can be ethically problematic, and audit data should be collected for legitimate governance purposes.

Illustrative Case: A Regulated Digital Services Platform

Background

The following is an illustrative case. A growing financial-services organization operates services across three cloud regions. Development teams frequently request networks, managed databases, Kubernetes namespaces, and observability components. Initially, each team used its own Terraform repositories and manual cloud-console procedures. Production changes were often approved through informal conversations.

Situation and Problem

The organization experienced inconsistent encryption settings, abandoned development resources, unclear ownership of cloud accounts, and lengthy security reviews. An incident revealed that the repository history did not match the resources that had actually been deployed. The organization needed faster delivery without weakening auditability or regulatory controls.

Analysis

An assessment identified four root causes. First, infrastructure modules had no stable product ownership. Second, CI pipelines mixed validation and production execution, allowing a developer-controlled runner to hold broad credentials. Third, there was no standardized plan-review-approval record. Fourth, teams were offered infrastructure primitives rather than usable service journeys, so they bypassed modules when those modules were difficult to use.

The organization decided not to pursue unrestricted auto-approval. Its risk profile required human authorization for production data stores and identity changes, while allowing automated delivery for low-risk development resources.

Approach and Strategy

A platform team created versioned Terraform modules for networks, databases, and service environments. Each module encoded encryption, tagging, backup, and ownership defaults. A service catalog exposed supported parameters, while a policy engine rejected unapproved regions, public database endpoints, missing cost centers, and excessive instance sizes.

Pull requests triggered formatting, validation, security scans, unit tests, and a generated plan. The plan was attached to the review record. Development environments could be applied automatically after successful checks. Staging required an environment-owner approval. Production required two designated approvals for database or identity changes and used a protected runner with short-lived cloud credentials.

The organization also introduced a GitOps repository for Kubernetes environment configuration. Application pipelines produced signed images; a change to the environment repository updated the approved image reference. A controller reconciled the cluster, while monitoring determined whether a canary should continue. Terraform remained responsible for foundational cloud resources, whereas the GitOps controller managed the desired Kubernetes configuration.

Outcome

The new model reduced ad hoc console changes and made infrastructure ownership visible. Teams obtained standard environments more quickly because common decisions were encoded in modules and templates. Security and audit teams could inspect policy results, approvals, and execution records without reconstructing events from chat messages. The platform did not eliminate exceptions: unusual workloads still required architectural review, and some changes used forward fixes rather than rollback because data transformations were irreversible.

Learning Points

The case demonstrates that automation alone does not create a platform. Value emerged from the combination of reusable technical components, risk-based approvals, secure execution, clear ownership, and user-oriented interfaces. It also shows why Terraform, Ansible, GitOps, and platform engineering should be integrated selectively rather than forced into a single toolchain. Different layers may require different control loops and delivery mechanisms.

Challenges, Limitations, and Common Mistakes

A frequent mistake is treating a green pipeline as proof of operational safety. Tests may omit provider failures, quota limits, data dependencies, or business impact. Another is allowing the pipeline to run `apply` from arbitrary branches or to regenerate a plan after approval without renewed review.

State management is another source of failure. Concurrent applies, unprotected state files, manual state edits, and inconsistent workspaces can create destructive confusion. Teams should design state boundaries deliberately and use locking and recovery procedures.

Over-automation creates risk when every change is automatically approved, while under-automation creates slow queues and encourages bypasses. The appropriate balance depends on blast radius, reversibility, service criticality, and confidence in testing. Similarly, excessive platform abstraction can hide important behavior from engineers. A platform should simplify consumption without making diagnosis impossible.

GitOps may produce reconciliation loops, deployment storms, or repeated restoration of an unsafe state. Platform teams must provide pause mechanisms, emergency access, drift visibility, and tested recovery. Progressive delivery can also fail if monitoring measures technical symptoms but not customer outcomes.

Finally, organizations sometimes confuse standardization with uniformity. A golden path should encode proven defaults while permitting controlled variation. A platform that cannot accommodate legitimate requirements will be bypassed, weakening both governance and developer experience.

Emerging Directions

Several developments are shaping this field. Policy-as-code is becoming more integrated with pull requests, cloud organization controls, and runtime admission systems. Software supply-chain security increasingly connects signed source, provenance, dependency scanning, artifact verification, and deployment authorization. Infrastructure testing is moving toward ephemeral environments and contract tests that validate module interfaces as well as individual resources.

AI-assisted tools can summarize plans, identify unusual changes, generate policy suggestions, and help diagnose failed runs. Their output should be treated as advisory. An AI system may misunderstand a destructive replacement, overlook a regulatory constraint, or expose sensitive configuration through an external service. Human accountability, deterministic checks, and protected execution remain necessary.

Internal platforms are also moving toward paved interfaces that span infrastructure, application deployment, observability, security, and cost management. The strategic challenge is to maintain a coherent developer experience without creating a monolithic platform that becomes difficult to evolve. Modular platform capabilities, clear APIs, and product-oriented ownership are likely to remain important.

Best-Practice Principles

A robust IaC delivery system generally follows these principles:

  • Keep desired configuration, policy, and reusable modules under version control.
  • Generate a visible plan or preview from the exact commit under review.
  • Separate validation, approval, execution, and verification responsibilities according to risk.
  • Use protected runners, least privilege, short-lived credentials, encrypted state, and state locking.
  • Treat rollback as a design problem and distinguish reversible changes from irreversible migrations.
  • Record approvals, plans, execution identities, outcomes, and audit evidence.
  • Prefer reusable modules and golden paths, but provide a controlled exception process.
  • Design self-service around complete lifecycle operations, not only provisioning.
  • Use progressive delivery where blast radius can be reduced through staged exposure.
  • Measure platform outcomes such as lead time, change failure rate, recovery time, adoption, cost, and developer effort.

Chapter Summary

CI/CD turns IaC changes into a repeatable lifecycle of validation, planning, review, approval, application, verification, and audit. Terraform plans and Ansible previews make intended changes visible, but neither replaces risk-based judgment. GitOps extends version-controlled desired state with automated reconciliation, commonly using pull-based agents that reduce direct deployment access while introducing drift and emergency-management considerations.

Platform engineering packages IaC, policies, workflows, and operational defaults into an internal developer platform. Self-service infrastructure and golden paths reduce cognitive load when they are easy to use, governed by guardrails, and maintained as products. Progressive delivery limits blast radius by releasing changes in stages and using reliable health signals. The strategic objective is not maximum automation; it is trustworthy, observable, secure, and efficient change at organizational scale.

Key Terms

CI/CD: Practices and automated workflows for integrating, validating, releasing, and deploying software or infrastructure changes.

Infrastructure as Code: The declarative or procedural management of infrastructure through machine-readable configuration.

Plan: A preview of infrastructure actions proposed by a tool such as Terraform before execution.

Drift: A difference between declared configuration and the actual state of an environment.

State: Recorded information that helps an IaC tool relate configuration to managed resources.

GitOps: An operating model in which version-controlled desired state drives automated reconciliation of environments.

Reconciliation: The process of comparing actual state with desired state and correcting differences.

Pull-based delivery: A model in which an agent in the target environment retrieves and applies approved configuration.

Push-based delivery: A model in which an external pipeline connects to a target environment and deploys changes.

Platform engineering: The discipline of building internal platforms that provide reusable, governed capabilities to development teams.

Internal Developer Platform: An internal product combining tools, workflows, interfaces, and controls for developer self-service.

Self-service infrastructure: Provisioning and operating infrastructure through approved interfaces without manual platform-team intervention for every request.

Golden path: A recommended, supported route for completing a common engineering task.

Policy as code: Machine-evaluable rules that enforce security, compliance, architecture, or cost requirements.

Progressive delivery: Releasing a change gradually while using feedback to decide whether to continue, pause, or reverse it.

Canary release: A staged release that exposes a change to a limited population before wider deployment.

Immutable artifact: A versioned build output whose contents do not change after publication.

Idempotence: The property that repeating an operation produces the intended stable result without unintended additional changes.

Blast radius: The extent of systems, users, or resources affected by a failure or change.

Audit trail: A record linking actions, identities, approvals, timestamps, inputs, and outcomes for accountability.

Review Questions

Short Answer Questions

1. What is the purpose of generating a Terraform plan before approval?

2. How does GitOps differ from a conventional push-based deployment pipeline?

3. Why is Terraform state a security and governance concern?

4. Define a golden path in the context of platform engineering.

5. What is meant by progressive delivery?

Long Answer Questions

1. Explain the stages of a mature IaC pipeline from source change through audit, and discuss the purpose of each stage.

2. Compare CI/CD and GitOps as infrastructure delivery models, including their security and operational trade-offs.

3. Explain how an internal developer platform can provide self-service infrastructure without eliminating governance.

4. Discuss the role of platform engineering in reducing developer cognitive load and improving organizational scalability.

5. Analyze why rollback is not always a simple reversal of a previous commit, particularly for stateful systems and data migrations.

Analytical / Application Questions

1. A production plan proposes replacing a database instance and modifying its network policy. Design an approval, execution, verification, and recovery workflow for this change.

2. An organization’s GitOps controller repeatedly restores a misconfigured resource after an administrator applies an emergency fix. Analyze the conflict and propose a safe operating procedure.

3. A platform team has low adoption of its golden path because provisioning takes several days and supported options are highly restrictive. Recommend changes using the principles of self-service, governance, and platform-as-product thinking.

22. Multiple Choice Questions with Answers

Question 1

Which sequence best represents a governed IaC delivery workflow?

A. Apply, plan, review, validate, audit

B. Validate, plan, review, approve, apply, verify, audit

C. Review, apply, validate, approve, rollback

D. Audit, apply, merge, plan, verify

Correct Answer: B. Validate, plan, review, approve, apply, verify, audit

Question 2

Why should an approved Terraform plan be retained and linked to the commit under review?

A. To eliminate the need for testing

B. To ensure the executed change corresponds to the reviewed intent

C. To permit unrestricted state modification

D. To replace cloud audit logs

Correct Answer: B. To ensure the executed change corresponds to the reviewed intent

Question 3

Which characteristic most specifically distinguishes GitOps from merely storing IaC in Git?

A. Use of multiple programming languages

B. Continuous reconciliation of actual state with version-controlled desired state

C. Mandatory manual deployment from a laptop

D. Prohibition of automated testing

Correct Answer: B. Continuous reconciliation of actual state with version-controlled desired state

Question 4

A platform team exposes only approved regions, sizes, encryption settings, and tags through a provisioning interface. What is this primarily an example of?

A. Guardrailed self-service infrastructure

B. Uncontrolled infrastructure sprawl

C. Manual change management

D. Blue-green application deployment

Correct Answer: A. Guardrailed self-service infrastructure

Question 5

Which statement about rollback is most accurate?

A. Every infrastructure change can be reversed by reverting the Git commit

B. Rollback is unnecessary when a pipeline succeeds

C. Data migrations and destructive changes may require a forward fix or recovery procedure

D. Rollback should be designed only after an incident occurs

Correct Answer: C. Data migrations and destructive changes may require a forward fix or recovery procedure

Question 6

A pipeline passes syntax checks, but its plan opens a production database to the public internet. What should happen next?

A. Apply automatically because validation succeeded

B. Ignore the plan because syntax is correct

C. Reject or escalate the change through policy and security review

D. Delete the state file and retry

Correct Answer: C. Reject or escalate the change through policy and security review

Question 7

Which feature most directly supports progressive delivery?

A. Applying a high-impact change globally without observation

B. Releasing to a limited region or traffic segment and evaluating defined health signals

C. Removing monitoring to reduce pipeline complexity

D. Allowing every team to bypass the platform

Correct Answer: B. Releasing to a limited region or traffic segment and evaluating defined health signals

Question 8

Why should an internal developer platform be treated as a product?

A. Because it requires no users or maintenance

B. Because it has users, supported journeys, quality expectations, feedback, and a lifecycle

C. Because it must be implemented as a commercial product

D. Because product teams should own all cloud credentials

Correct Answer: B. Because it has users, supported journeys, quality expectations, feedback, and a lifecycle

Question 9

Which control best reduces the risk of a pipeline exposing long-lived cloud credentials?

A. Embedding credentials in Terraform variables

B. Printing credentials in job logs for auditing

C. Using short-lived credentials and least-privilege workload identity

D. Storing credentials in a public repository

Correct Answer: C. Using short-lived credentials and least-privilege workload identity

Question 10

A second Ansible run reports unintended changes to the same hosts. Which property is likely missing or defective?

A. Idempotence

B. Canary routing

C. Artifact signing

D. Branch protection

Correct Answer: A. Idempotence

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 12 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 13
Operations, Governance and Infrastructure Analytics

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

13. Operations, Governance and Infrastructure Analytics

Introduction and Context

Infrastructure as Code (IaC) is often introduced as a way to provision servers, networks, platforms, and cloud services through declarative or procedural code. That description is accurate but incomplete. In a production environment, infrastructure does not end when a Terraform plan is applied or an Ansible playbook completes. It must be operated, observed, governed, secured, measured, upgraded, recovered, and eventually retired. The operational life of infrastructure is therefore as important as its initial construction.

The central challenge is that infrastructure exists simultaneously in several forms: the desired state expressed in repositories, the state recorded by IaC tools, the configuration held by providers, and the runtime condition observed in production. These forms can diverge. A console change, emergency intervention, failed deployment, provider update, expired certificate, or undocumented dependency may create drift between what an organization intends and what it actually operates.

For postgraduate practitioners, operations and governance should be viewed as a management system rather than a collection of tools. This system connects technical evidence with organizational decisions. Drift data informs control effectiveness; observability data informs reliability; cost and utilization data inform capacity planning; compliance evidence informs risk treatment; and delivery metrics inform the design of engineering processes. Terraform and Ansible provide mechanisms for managing infrastructure, but governance determines how those mechanisms are used responsibly at scale.

Learning Objectives

By the end of this chapter, the learner should be able to:

1. Distinguish configuration drift, state drift, operational failure, and technical debt.

2. Design a lifecycle for detecting, analysing, and remediating infrastructure drift.

3. Relate infrastructure observability to incident response, service reliability, and recovery.

4. Develop upgrade, disaster-recovery, and decommissioning strategies for IaC-managed environments.

5. Select and interpret deployment, failure, lead-time, compliance, utilization, and cost metrics.

6. Apply governance, FinOps, and risk-based decision-making to infrastructure operations.

7. Evaluate the organizational and ethical consequences of infrastructure automation.

Infrastructure as a Living Operational System

A useful operating model separates four states. Desired state is the configuration declared in Terraform modules, variable files, policies, and Ansible roles. Recorded state is the metadata maintained by Terraform state or related inventories. Actual state is what cloud APIs, operating systems, networks, and managed services currently report. Observed state is the condition inferred from logs, metrics, traces, events, security findings, and user experience.

These states should normally converge, but they are not identical. Terraform may know that a virtual network exists without knowing whether an application can reach its database. Ansible may report that a package is installed while an application remains unhealthy because of a certificate or external dependency. Conversely, observability may reveal a performance problem that is not represented by any configuration difference.

This distinction supports an important principle: reconciliation is necessary but not sufficient. Reapplying code can restore declared configuration, yet it cannot by itself establish business availability, data correctness, security compliance, or acceptable cost. Mature operations combine IaC reconciliation with service-level monitoring and human judgement.

Important Concepts and Terminology

Drift is an unintended difference between the declared or approved configuration and the actual environment. Drift may be intentional, such as an approved emergency change not yet codified, or unauthorized and accidental.

Terraform state is the tool’s record of managed resources and their relationships. It is not a complete inventory of the environment and should not be treated as a universal source of truth.

Reconciliation is the process of comparing intended and actual conditions and taking action to restore an approved state.

Observability is the ability to infer internal system conditions from externally produced signals, principally metrics, logs, traces, and events.

Incident is an unplanned interruption or degradation of a service. Problem management investigates underlying causes and seeks to prevent recurrence.

Recovery Point Objective (RPO) is the maximum tolerable amount of data loss measured in time. Recovery Time Objective (RTO) is the target time within which a service should be restored.

Technical debt is the future cost created by expedient, obsolete, undocumented, or poorly designed technical decisions. It includes infrastructure debt such as unsupported operating systems, duplicated modules, manual exceptions, and weak recovery procedures.

FinOps is a collaborative operating discipline that connects engineering, finance, and business teams to manage cloud value, accountability, and cost.

Control is a policy, process, or technical mechanism designed to reduce risk. Evidence is the verifiable record that a control operated as intended.

Decommissioning is the controlled removal of infrastructure, data, identities, dependencies, and operational obligations when a service is retired.

Drift Detection and Reconciliation

Drift management begins with a clear change authority. Every modification should have an identifiable source: a reviewed pull request, an approved pipeline, a documented emergency procedure, or an explicitly authorized manual action. Without this principle, detection becomes a technical exercise with no governance consequence.

Terraform commonly detects differences during refresh and planning. A scheduled plan, executed with read-only credentials where possible, can produce a drift report without applying changes. The report should classify changes rather than simply count them. A change to a tag may be low risk; a changed security-group rule, encryption setting, route, or identity policy may be critical. Ansible supports related controls through idempotent playbooks, check mode, configuration audits, and scheduled compliance scans.

A practical drift process contains five stages:

1. Discover: compare desired, recorded, actual, and observed states.

2. Classify: determine whether the difference is authorized, harmless, urgent, or dangerous.

3. Investigate: identify the actor, cause, dependency, and business effect.

4. Decide: import, codify, revert, quarantine, or accept the exception with an expiry date.

5. Verify: rerun checks and confirm that service, security, and compliance conditions remain satisfactory.

Blindly applying Terraform to eliminate drift is unsafe. It may destroy an emergency fix, interrupt a dependent service, or overwrite a legitimate provider-side change. Similarly, using `terraform import` merely to remove an alert can place an unmanaged resource under incomplete configuration. Reconciliation must be preceded by impact analysis and, for material systems, a change record.

State management is itself a governance concern. Remote state should have access control, encryption, versioning, locking, backup, and separation between environments. Secrets should not be exposed through state files, plan artifacts, logs, or Ansible output. Workspaces, accounts, subscriptions, or projects should be separated according to blast radius and authorization boundaries rather than used merely as naming conventions.

Observability, Incidents, and Operational Learning

Observability extends IaC beyond provisioning. Infrastructure signals should explain whether a service is available, fast, secure, and economically sustainable. Metrics describe numerical behaviour; logs provide event detail; traces show requests across components; and events record changes such as deployments, scaling actions, policy violations, or provider failures.

Instrumentation should be connected to service objectives. CPU utilization alone is not an availability measure. A more useful model relates infrastructure signals to indicators such as request success rate, latency, saturation, queue depth, storage growth, certificate validity, replication lag, and dependency health. Dashboards should distinguish symptoms from causes and should provide enough context for an operator to act.

Incident response normally proceeds through detection, triage, containment, diagnosis, recovery, communication, and review. IaC repositories, pipeline logs, cloud audit records, and configuration histories are valuable investigative evidence. A change correlation mechanism can reveal that a latency increase began shortly after a network rule, instance type, or operating-system update was deployed.

Incident response should not depend on a single expert. Runbooks should state prerequisites, decision points, rollback conditions, escalation contacts, and validation steps. Ansible may automate safe diagnostic or recovery tasks, while Terraform may restore infrastructure after an approved decision. Automation should include safeguards such as approvals, scope restrictions, backups, and dry-run modes.

A blameless post-incident review does not mean that accountability disappears. It means that analysis concentrates on system conditions: ambiguous ownership, inadequate testing, weak permissions, missing alerts, excessive coupling, or unrealistic recovery objectives. Corrective actions should have owners, due dates, and verification criteria. Otherwise, the review becomes a narrative rather than a control improvement.

Disaster Recovery and Resilience Engineering

Disaster recovery is the coordinated restoration of services after events such as region failure, ransomware, accidental deletion, provider outage, or severe operator error. IaC improves repeatability, but recovery also depends on data protection, identity availability, DNS, certificates, external integrations, licensing, and human procedures.

A recovery design should begin with business impact analysis. Critical services require explicit RTO and RPO targets, acceptable degradation modes, recovery dependencies, and authority to declare a disaster. Common patterns include backup and restore, pilot light, warm standby, and active-active operation. The more rapidly a service must recover, the greater the likely cost and operational complexity.

Terraform can recreate networks, compute resources, policies, and managed services, while Ansible can configure operating systems and applications after provisioning. However, recovery code must be tested in an environment that resembles production. A successful Terraform plan does not prove that backups are restorable or that an application can reconnect to its dependencies.

Recovery testing should measure actual RTO and RPO, not merely confirm that documentation exists. Tests may be tabletop exercises, isolated restoration tests, partial failovers, or full simulations. Results should be recorded, including failed assumptions and manual steps. Immutable or logically isolated backups, separate credentials, and protection against simultaneous deletion are especially important for ransomware scenarios.

Upgrades, Change, and Decommissioning

Infrastructure upgrades include provider versions, Terraform and Ansible versions, modules, operating systems, runtimes, database engines, network components, and security agents. An upgrade is not simply a version edit. It is a change to behaviour, compatibility, supportability, and risk.

A disciplined upgrade process inventories dependencies, reads release notes, reviews breaking changes, updates lock files, runs static checks and tests, generates a plan, and applies the change progressively. Environments should be promoted from development to test to production using the same artefact or module version where possible. Rollback must be realistic: some database migrations and provider changes are irreversible, so the strategy may be forward-fix, backup restoration, or traffic redirection rather than a simple version reversal.

Configuration and module repositories should have ownership, semantic versioning or an equivalent release discipline, automated tests, documentation, and a supported-version policy. Pinning versions improves reproducibility, but indefinite pinning creates security and technical debt. The appropriate practice is controlled currency: versions are constrained, monitored, and upgraded through a predictable calendar.

Decommissioning requires more than deleting a resource block. The organization should identify data retention obligations, backups, DNS records, certificates, service accounts, firewall rules, monitoring alerts, cost commitments, contracts, and downstream consumers. A staged process may be: announce retirement, stop new traffic, archive required data, disable access, observe for unexpected dependencies, remove resources, verify deletion, and close operational records. Destructive actions should require explicit approval and an auditable record.

Metrics for Infrastructure Decisions

Metrics become useful when they support a decision. A dashboard with many numbers may still provide little management value if definitions, owners, baselines, and action thresholds are absent.

Delivery and reliability can be examined through deployment frequency, lead time for change, change failure rate, and time to restore service. Deployment frequency indicates delivery flow; lead time measures the time from approved change to production; change failure rate measures the proportion of changes causing remediation, rollback, or material degradation; and restore time measures response effectiveness. These metrics should be interpreted together. Increasing deployment frequency while failure rate and restore time rise may represent acceleration without control.

Infrastructure-specific measures include drift age, percentage of resources managed by approved code, failed plan or apply rate, mean time to remediate critical findings, backup success and restoration success, patch compliance, and percentage of unsupported components. A compliance percentage is meaningful only when the population, control definition, exception process, and evidence period are clear.

Utilization metrics support capacity planning. CPU, memory, storage, network throughput, IOPS, queue depth, and accelerator utilization should be examined against demand patterns, performance objectives, and scaling limits. Average utilization can hide peaks, noisy neighbours, throttling, and latency effects. Capacity decisions should therefore use percentiles, seasonality, forecast uncertainty, and failure headroom.

Cost metrics should connect expenditure with a unit of value: cost per transaction, environment, customer, team, workload, or service. Allocation requires reliable tags, account structure, chargeback or showback rules, and treatment of shared costs. FinOps is not merely cost cutting. An under-provisioned service may reduce infrastructure cost while increasing lost revenue, operational effort, or customer dissatisfaction.

Risk-based decisions combine likelihood, impact, detectability, recovery capability, and control strength. For example, a rarely used but internet-facing administrative endpoint may deserve more attention than a heavily used internal resource if compromise would have severe consequences. Risk registers should link findings to owners, treatment decisions, residual risk, and review dates.

Governance Architecture and Organizational Practice

Governance should create guardrails without making legitimate delivery impossible. Preventive controls may include policy-as-code, approved modules, restricted identities, mandatory encryption, network boundaries, and protected branches. Detective controls include drift scans, audit-log analysis, vulnerability reports, cost anomaly detection, and compliance dashboards. Corrective controls include automated remediation, ticket creation, quarantine, and recovery procedures.

Terraform policy frameworks and cloud-native policy engines can block insecure plans, while Ansible can enforce operating-system baselines. Yet controls must be tested for false positives, bypass paths, and unintended operational effects. An emergency exception process should be fast but time-limited, with retrospective review. Permanent exceptions indicate that the standard may be impractical or that risk has been consciously accepted.

Responsibilities should be explicit. Platform teams may provide modules and guardrails; product teams remain accountable for service behaviour and business outcomes; security teams define control requirements; finance teams establish cost practices; and leadership determines risk appetite and funding. A RACI matrix, service catalogue, ownership metadata, and escalation paths reduce ambiguity.

Governance also has an ethical dimension. Automated infrastructure decisions can affect privacy, service access, employment, and public safety. Teams should minimize unnecessary data collection, protect secrets, preserve auditability, and ensure that automation does not silently create discriminatory or unsafe outcomes. Human approval is particularly important for destructive actions, high-impact access changes, and decisions involving regulated data.

Illustrative Case: A Retail Platform Under Operational Stress

Background

A fictional online retailer, Northstar Market, operates its customer-facing platform across two cloud regions. Terraform manages networks, load balancers, databases, and Kubernetes clusters. Ansible configures supporting virtual machines and security agents. The organization has grown rapidly, but its operating practices have not matured at the same rate.

Situation

Over one quarter, cloud expenditure increased by 32 percent, deployment-related incidents became more frequent, and a database failover test exceeded the stated four-hour RTO. A scheduled Terraform plan also reported changes to security-group rules and database parameters. Engineers initially considered the findings harmless because the application appeared healthy.

Analysis

Investigation found four interacting conditions. First, a production engineer had made an emergency console change during a previous incident and had not codified it. Second, an Ansible role and a cloud-init script applied different operating-system settings. Third, resource tags were incomplete, preventing accurate allocation of shared costs. Fourth, the disaster-recovery procedure recreated infrastructure but did not restore secrets, DNS records, or a dependent payment integration.

The organization’s metrics had also been interpreted separately. Deployment frequency had increased, but change failure rate and time to restore had increased more sharply. Average cluster utilization looked acceptable, yet peak memory pressure caused scaling delays. The apparent cost problem was partly idle development capacity and partly inefficient database storage, not simply excessive production compute.

Approach

Northstar established an operational improvement programme. It introduced scheduled read-only drift plans and classified findings by security and service impact. The emergency console change was reviewed, tested, and codified in a versioned module. Conflicting Ansible and cloud-init responsibilities were removed in favour of one authoritative configuration path.

The team then introduced mandatory ownership and cost metadata, a service catalogue, and dashboards showing cost per order, peak resource utilization, deployment failure rate, drift age, and restoration test results. Policy checks blocked unencrypted storage and public administrative access, while an expiring exception process supported urgent changes.

For resilience, the team documented dependencies, stored recovery material separately, and conducted a restoration exercise in an isolated account. The exercise revealed that the original RTO was unrealistic without a warm standby. Leadership therefore accepted a revised RTO for one service, funded warm standby for the payment path, and prioritized recovery improvements according to business impact.

Outcome

The immediate result was not a single technology replacement but improved decision quality. Drift findings became actionable rather than alarming noise, cost ownership improved, and recovery testing exposed weaknesses before a disaster. Some infrastructure remained temporarily more expensive because resilience was deliberately increased, but the organization could explain that cost in relation to risk and business value.

Learning Points

The case illustrates that IaC maturity is operational and organizational. Code, policies, metrics, ownership, incident learning, and recovery exercises must reinforce one another. A technically correct plan can still produce a strategically poor decision if it ignores dependencies, risk appetite, or the economics of service delivery.

Challenges, Limitations, and Common Mistakes

A common mistake is treating drift as a purely technical defect. Some drift is evidence of an unrecorded business decision; the correct response may be to codify it rather than revert it. Another mistake is measuring compliance as a single percentage without assessing control quality or meaningful exceptions.

Automation can amplify errors. A flawed module or playbook may change hundreds of resources, and a broad remediation role may create an outage while attempting to improve compliance. Scope limits, approvals, testing, progressive rollout, and recovery plans are necessary safeguards.

Metrics can also be gamed. Teams may reduce lead time by splitting changes artificially, report high deployment frequency through low-value changes, or suppress incident classification. Measurement systems should therefore combine quantitative indicators with qualitative review and outcome measures.

Finally, technical debt is often postponed because it lacks an immediate feature benefit. Unsupported versions, undocumented exceptions, and weak recovery procedures accumulate interest through slower delivery and greater failure impact. Debt registers should identify risk, estimated effort, dependencies, and a repayment strategy rather than merely listing complaints.

Current Developments and Best Practices

Contemporary infrastructure operations increasingly combine policy-as-code, continuous compliance, software supply-chain controls, OpenTelemetry-based observability, automated cost anomaly detection, and platform engineering. Generative AI is being used to summarize incidents, suggest policy explanations, classify alerts, and assist with runbook execution. Such systems should remain advisory or tightly bounded for high-impact actions because generated recommendations may be incomplete, insecure, or based on misleading telemetry.

Effective practice includes the following principles:

  • Keep desired configuration, state, secrets, and evidence protected and appropriately separated.
  • Make one clearly defined mechanism authoritative for each configuration domain.
  • Run drift and compliance checks continuously, but classify findings by impact.
  • Treat observability as a service capability, not merely a collection of dashboards.
  • Test restoration, failover, upgrades, and destructive procedures before they are urgently needed.
  • Use progressive delivery, immutable or reproducible artefacts, and explicit rollback or forward-fix plans.
  • Connect utilization and cost to workload value and service objectives.
  • Give every critical resource and service an owner, lifecycle status, and support classification.
  • Make exceptions visible, risk-assessed, approved, and time-limited.
  • Review metrics as a portfolio of evidence rather than optimizing one number.

Chapter Summary

Operational IaC extends beyond provisioning into continuous reconciliation, observability, governance, resilience, measurement, and lifecycle management. Drift should be detected through comparison of desired, recorded, actual, and observed states, then classified and remediated safely. Observability links infrastructure behaviour to service outcomes and strengthens incident response. Disaster recovery requires tested restoration of both infrastructure and dependencies, with RTO and RPO grounded in business impact.

Upgrades should be controlled and reproducible, while decommissioning must address data, identity, dependencies, monitoring, contracts, and evidence. Deployment, failure, lead-time, compliance, utilization, and cost metrics become valuable when they have clear definitions and support decisions. FinOps, capacity planning, and risk management should balance cost with reliability, security, and business value. The mature organization treats Terraform and Ansible not as isolated automation tools but as components of a governed operational system.

Key Terms

Drift: An unintended difference between approved desired configuration and actual infrastructure.

Reconciliation: Comparison and correction of differences between intended and actual state.

Terraform state: A record of resources and relationships managed by Terraform.

Observability: The ability to infer system condition from metrics, logs, traces, and events.

Incident: An unplanned interruption or degradation of a service.

RTO: The target time for restoring a service after disruption.

RPO: The maximum acceptable period of data loss.

Technical debt: Future cost caused by expedient, obsolete, or poorly maintained technical decisions.

FinOps: A collaborative discipline for managing cloud value, accountability, and cost.

Policy-as-code: Machine-readable rules that assess or enforce technical and governance requirements.

Control: A mechanism intended to reduce a particular risk.

Evidence: Verifiable information demonstrating that a control or process operated.

Capacity planning: Forecasting and preparing resources to meet future demand and resilience needs.

Change failure rate: The proportion of changes that cause incidents, remediation, rollback, or material degradation.

Decommissioning: Controlled retirement and removal of infrastructure and its associated obligations.

Exception: An authorized temporary departure from a standard or policy.

State drift: Divergence between recorded IaC state and provider or runtime state.

Blameless review: Incident analysis focused on system conditions rather than individual punishment.

Residual risk: Risk that remains after controls or treatments are applied.

Review Questions

Short Answer Questions

1. Why is Terraform state not a complete source of truth for infrastructure operations?

2. Distinguish RTO from RPO.

3. What is the purpose of classifying drift before remediation?

4. Name four signals commonly used in observability.

5. Why should cloud cost be related to a unit of business value?

Long Answer Questions

1. Explain a complete drift detection and reconciliation process for a Terraform-managed environment.

2. Discuss how observability, incident response, and post-incident learning reinforce one another.

3. Compare backup-and-restore, warm-standby, and active-active disaster-recovery approaches.

4. Develop a governance model showing how platform, product, security, finance, and leadership responsibilities can be coordinated.

5. Explain how deployment, failure, lead-time, compliance, utilization, and cost metrics should be interpreted together.

Analytical / Application Questions

1. A scheduled plan reports that a production firewall differs from code, while monitoring shows no outage. Design the investigation and decision process before any apply operation.

2. A company reduces infrastructure spending by 20 percent but experiences higher latency and longer incident recovery. Use reliability, utilization, and FinOps reasoning to evaluate the decision.

3. Design a decommissioning and disaster-recovery exercise for a critical service managed by Terraform and Ansible. Identify evidence that would demonstrate operational readiness.

22. Multiple Choice Questions with Answers

Question 1

Which statement best describes infrastructure drift?

A. A planned deployment that changes a resource according to approved code

B. An unintended difference between approved desired configuration and actual infrastructure

C. A temporary increase in application traffic

D. A backup copied to a separate region

Correct Answer: B. An unintended difference between approved desired configuration and actual infrastructure

Question 2

Why should drift normally be classified before Terraform applies a correction?

A. Because Terraform cannot create plans without classification

B. Because every drift event is necessarily harmless

C. Because an automatic correction may remove an emergency fix or cause an outage

D. Because classification replaces the need for version control

Correct Answer: C. Because an automatic correction may remove an emergency fix or cause an outage

Question 3

Which combination most directly supports observability?

A. Metrics, logs, traces, and events

B. Budgets, invoices, contracts, and tags

C. Modules, variables, providers, and workspaces

D. Passwords, certificates, licenses, and backups

Correct Answer: A. Metrics, logs, traces, and events

Question 4

An organization wants to know how much data it may lose during a disaster. Which measure is most relevant?

A. RTO

B. Lead time for change

C. RPO

D. Deployment frequency

Correct Answer: C. RPO

Question 5

Which action is most appropriate when a production console change was authorized as an emergency?

A. Ignore it permanently because the service is healthy

B. Immediately delete the resource without investigation

C. Review, test, and codify the approved change in managed configuration

D. Disable all future console access without recording the change

Correct Answer: C. Review, test, and codify the approved change in managed configuration

Question 6

Why can average resource utilization be misleading for capacity planning?

A. It always exaggerates peak demand

B. It may hide peak loads, throttling, seasonality, and insufficient failure headroom

C. It measures only software licensing costs

D. It cannot be collected from cloud systems

Correct Answer: B. It may hide peak loads, throttling, seasonality, and insufficient failure headroom

Question 7

Which metric pairing most clearly indicates delivery acceleration accompanied by reduced stability?

A. Higher deployment frequency with higher change failure rate

B. Lower cost per transaction with lower storage use

C. Higher backup success with lower drift age

D. Lower lead time with lower incident duration

Correct Answer: A. Higher deployment frequency with higher change failure rate

Question 8

What is a major limitation of treating compliance as one percentage?

A. Percentages cannot be calculated automatically

B. It may hide the control definition, population, evidence quality, and exceptions

C. Compliance is unrelated to governance

D. A high percentage always proves low risk

Correct Answer: B. It may hide the control definition, population, evidence quality, and exceptions

Question 9

Which activity is essential during decommissioning?

A. Deleting only the primary compute resource

B. Removing resources without checking consumers or retention duties

C. Identifying data, identities, dependencies, monitoring, and contractual obligations

D. Disabling all audit logging before retirement

Correct Answer: C. Identifying data, identities, dependencies, monitoring, and contractual obligations

Question 10

What is the central purpose of FinOps in infrastructure operations?

A. To minimize every cloud expense regardless of service impact

B. To connect engineering, finance, and business decisions around cloud value and cost

C. To replace infrastructure monitoring with financial reports

D. To prevent all use of elastic capacity

Correct Answer: B. To connect engineering, finance, and business decisions around cloud value and cost

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 13 of 14

Infrastructure as Code (IaC) with Terraform/Ansible

Chapter 14
Enterprise Strategy, Ethics and Emerging IaC

Post Graduate Program in Dev Ops
Infrastructure as Code (IaC) with Terraform/Ansible   •   Post Graduate

14. Enterprise Strategy, Ethics and Emerging IaC

Introduction and Context

Infrastructure as Code (IaC) is often introduced as a technical method for defining servers, networks, policies, and platform services in machine-readable form. At enterprise scale, however, IaC becomes more than a provisioning technique. It becomes an operating model for expressing infrastructure decisions, governing change, distributing responsibility, and connecting technology investment with organizational strategy.

Terraform, Ansible, and related tools allow organizations to replace undocumented manual procedures with repeatable, reviewable, and automatable workflows. Yet automation does not automatically produce good outcomes. Poorly designed automation can reproduce insecure assumptions, accelerate waste, conceal accountability, or create dependence on a particular vendor or platform. Consequently, enterprise IaC requires a balanced approach involving architecture, governance, economics, professional ethics, workforce development, and continuous evaluation of emerging technologies.

This chapter examines how organizations can adopt IaC strategically, establish operating models and standards, construct business cases, and address questions of accountability, sustainability, vendor lock-in, and ethical automation. It also considers AI-assisted coding, autonomous remediation, intent-based infrastructure, and other developments that may alter the relationship between engineers and provisioning systems.

Learning Objectives

After completing this chapter, the learner should be able to:

1. Develop an enterprise roadmap for IaC adoption and transformation.

2. Compare centralized, federated, and platform-oriented IaC operating models.

3. Design standards, governance controls, and decision rights for Terraform and Ansible environments.

4. Construct a business case using cost, risk, productivity, resilience, and strategic-value arguments.

5. Evaluate vendor lock-in, workforce, sustainability, ethical, and accountability issues.

6. Assess AI-assisted infrastructure coding, autonomous remediation, and intent-based infrastructure.

7. Analyze an enterprise case and recommend an appropriate implementation strategy.

From Automation Tool to Enterprise Capability

In a small team, IaC may mean a Terraform repository, an Ansible playbook, and a deployment pipeline. In a large organization, it includes a lifecycle: requirements, design, code creation, testing, approval, deployment, monitoring, change management, and retirement. The enterprise capability therefore consists of people, processes, platforms, policies, and technical assets.

A useful distinction is between automation and industrialization. Automation performs a task without manual intervention. Industrialization creates a repeatable system in which the task is standardized, measured, governed, and continuously improved. An organization that allows every team to write independent modules may be highly automated but poorly industrialized. It can suffer from incompatible conventions, duplicated effort, inconsistent security, and uncontrolled cloud expenditure.

IaC also changes the location of knowledge. In a manual environment, operational expertise may reside in individuals who know which console settings to change. In an IaC environment, knowledge is expected to reside in code, module interfaces, documentation, version history, and policy. This improves organizational memory, but only when code is understandable, maintained, and accessible to the people responsible for operating it.

The strategic question is not simply, “Which tool should be adopted?” It is, “What kind of infrastructure operating capability does the organization need, and how can IaC support that capability?”

Enterprise Adoption as a Transformation Roadmap

IaC adoption should be staged rather than treated as a single migration project. A practical roadmap normally begins with discovery and alignment, proceeds through controlled adoption, and eventually develops into a platform and governance capability.

Stage One: Establish the Baseline

The organization first inventories infrastructure, deployment methods, ownership, compliance obligations, and major sources of operational risk. It should identify which resources are manually created, which are already scripted, and which systems are too critical for immediate conversion. Baseline measures may include provisioning time, deployment failure rate, mean time to recover, configuration drift, change-related incidents, and infrastructure cost variance.

This stage should also identify stakeholders. Infrastructure, security, application development, finance, procurement, risk, audit, and business owners may have different expectations. IaC introduced solely by a technical team can later encounter resistance because it affects approval rights, operational roles, and financial controls.

Stage Two: Select Pilots with Strategic Value

A pilot should be significant enough to demonstrate value but bounded enough to control risk. Suitable candidates may include development environments, standardized application platforms, disaster-recovery environments, or repeatable cloud accounts. Highly regulated production systems with undocumented dependencies are usually poor first pilots.

The pilot should have explicit success criteria. For example, the organization may target a reduction in environment creation time, improved recovery reproducibility, or elimination of a defined class of configuration errors. A pilot that merely produces code without measurable operational improvement provides weak evidence for wider investment.

Stage Three: Standardize and Scale

Once a pilot demonstrates value, the organization develops reusable modules, playbook conventions, repository structures, testing practices, and pipeline controls. Terraform modules may provide approved network, identity, database, or compute patterns. Ansible roles may provide hardened operating-system configurations or application deployment procedures.

Scaling requires attention to interfaces. A module should expose only the variables that consumers legitimately need and should establish safe defaults. Excessive flexibility produces a module that is difficult to test and govern. Standardization should not remove all local choice; rather, it should establish a secure and supportable default while documenting exceptions.

Stage Four: Create a Platform Operating Model

At maturity, teams consume infrastructure through paved roads, templates, service catalogs, and self-service interfaces. A platform team maintains common capabilities, while product teams retain responsibility for the services they run. Governance becomes increasingly automated through policy-as-code, continuous validation, cost controls, and evidence collection.

Maturity should not be measured by the number of repositories or lines of code. More meaningful indicators include the percentage of infrastructure managed declaratively, the reliability of automated changes, the proportion of resources covered by ownership metadata, the speed of compliant delivery, and the reduction of unplanned manual intervention.

Operating Models and Decision Rights

An enterprise must decide who owns standards, modules, pipelines, exceptions, and production outcomes. Three operating models are common.

In a centralized model, a core infrastructure team owns most IaC code and deployment processes. This promotes consistency and may be appropriate where skills are scarce or regulatory control is strong. Its weakness is that a central team can become a bottleneck and may not understand every application domain.

In a federated model, individual product or business teams own their infrastructure code within enterprise-wide standards. A central enablement group provides frameworks, reference architectures, training, and guardrails. This model improves domain responsiveness but requires strong capability management and consistent controls.

A platform-oriented model treats infrastructure as an internal product. The platform team offers documented, versioned capabilities, such as a compliant Kubernetes environment, a database service, or an application delivery pattern. Product teams consume these capabilities through self-service interfaces. The platform team is accountable for usability, reliability, and lifecycle management; consuming teams remain accountable for the applications and data they deploy.

These models are not mutually exclusive. An enterprise may centralize identity and networking, federate application deployment, and use a platform model for common runtime services. The important issue is clarity of decision rights. A responsibility matrix should identify who designs a standard, approves an exception, maintains a module, responds to a failed deployment, and accepts residual risk.

Standards, Governance, and Control Design

Governance should enable safe delivery rather than simply add approval steps. A strong control environment places routine controls in automated pipelines and reserves human judgment for unusual or consequential decisions.

Standards commonly address naming, tagging, repository structure, module versioning, secrets handling, state management, environment separation, identity permissions, logging, backup, network exposure, encryption, and retirement. They should be written as enforceable and testable requirements wherever possible. “Use secure configurations” is less useful than a policy stating that storage resources must not be publicly accessible unless an approved exception exists.

A mature IaC pipeline may include the following control layers:

  • formatting and syntax validation;
  • static analysis and security scanning;
  • unit and integration tests;
  • plan review and policy-as-code checks;
  • approval based on risk and environment;
  • controlled application of changes;
  • post-deployment verification and drift detection.

Terraform state deserves particular governance attention because it may contain sensitive values and represents the organization’s understanding of managed resources. Remote state should use access control, encryption, locking, backup, and an appropriate separation of environments. Ansible requires equivalent care for inventories, credentials, become privileges, vault keys, and execution logs.

Change approval should be risk-based. A routine change to a versioned, tested module may be automatically approved, whereas a change to an identity boundary, production network route, or regulated data store may require additional review. Approval should not be confused with accountability: the person who clicks an approval button is not necessarily the only person responsible for the design or outcome.

Building the Business Case

The business case for IaC should not rely solely on labor reduction. In many organizations, the most important benefits are improved reliability, faster recovery, auditability, reduced deployment risk, and the ability to provide environments consistently across regions or teams.

A basic economic model can compare the total cost of the current operating approach with the cost of IaC adoption and operation. Costs include tooling, training, platform engineering, migration, testing, support, and potentially duplicated operation during transition. Benefits may include reduced manual effort, fewer incidents, shorter delivery cycles, lower recovery costs, and improved resource utilization.

Financial analysis should distinguish between efficiency, avoidance, and enablement. Efficiency reduces the resources required for an existing activity. Avoidance prevents expected losses, such as outages or audit findings. Enablement creates new capacity, such as launching products in additional regions or allowing teams to provision compliant environments independently. Enablement benefits may be strategically important even when they are difficult to express as immediate savings.

A credible business case includes baseline data, assumptions, risks, sensitivity analysis, and a measurement plan. For example, a claim that IaC will reduce provisioning time by 80 percent should identify the current process, the environments included, the dependencies excluded, and the conditions under which the target is measured. Benefits should be reviewed after implementation rather than treated as permanent assumptions.

Skills, Culture, and Organizational Change

IaC adoption changes professional roles. System administrators need software engineering practices such as version control, testing, code review, and modular design. Developers need greater awareness of operating-system configuration, networking, identity, resilience, and cost. Managers need to understand that a successful transformation may initially reduce visible delivery speed because teams are learning new practices and building foundations.

Training should be complemented by communities of practice, reference repositories, pairing, internal documentation, and accessible expert support. Organizations should avoid measuring engineers only by the volume of infrastructure code produced. A short, reusable module that prevents repeated errors may be more valuable than thousands of lines of duplicated configuration.

Cultural change is especially important where manual intervention has historically been treated as evidence of expertise. IaC may expose undocumented dependencies and challenge informal authority. Leaders should frame standardization as a means of reducing avoidable operational risk, not as a judgment on individuals. At the same time, automation must not remove the possibility of a controlled emergency response when a system is unavailable and the normal path cannot function.

Vendor Lock-In and Portability

IaC can reduce lock-in by making infrastructure definitions portable and visible, but it can also deepen lock-in. Terraform configurations may depend heavily on a cloud provider’s proprietary services, provider-specific resource models, or a particular state and workflow ecosystem. Ansible playbooks may be portable at the operating-system level while still depending on vendor-specific APIs or managed services.

Portability is not an absolute virtue. A multi-cloud design may increase complexity, reduce the benefits of specialized services, and create duplicated skills and controls. The relevant question is whether the organization has a deliberate level of dependence that matches its strategy.

A lock-in assessment should examine:

  • proprietary services and data formats;
  • exit costs and migration time;
  • availability of alternative providers;
  • portability of data and state;
  • contractual terms and support arrangements;
  • internal skills required to operate alternatives.

Mitigation may include abstraction at selected boundaries, open formats, documented export procedures, modular architecture, provider version pinning, and periodic recovery or migration exercises. Abstraction should be used selectively. Hiding every provider-specific feature behind a generic interface can create a lowest-common-denominator platform that satisfies no major workload well.

Ethics, Accountability, and Responsible Automation

Infrastructure automation affects users, employees, customers, and the public. Ethical analysis therefore extends beyond technical security. An automated decision to deny access, remove a resource, alter a network route, or scale down capacity may affect business continuity, privacy, safety, or employment.

Four principles are particularly important. Responsibility requires identifiable owners for automated systems and their outcomes. Transparency requires that significant actions can be explained through code, policy, logs, and decision records. Proportionality requires controls and automation intensity to match the potential harm. Contestability requires a means to challenge, reverse, or review an automated decision.

Automation can reproduce bias when its policies encode unequal assumptions. For instance, an access-control workflow that treats contractors as inherently untrusted may be unjustified if it is not based on role, risk, or actual need. Likewise, automated remediation that terminates resources based on incomplete cost data can disproportionately affect teams with unusual but legitimate workloads.

Ethical IaC also includes honesty in audit evidence. Automatically generated compliance reports should not be presented as proof that a control is effective if they only show that a configuration value exists. Organizations must distinguish between a control being configured, operating, and achieving its intended outcome.

Sustainability and Infrastructure Responsibility

IaC can support sustainability by making resource ownership visible, enforcing expiration policies for temporary environments, selecting efficient instance types, and enabling repeatable measurement. It can also increase consumption if automation makes it effortless to create idle environments or duplicate services.

Sustainability should be considered across the lifecycle: design, deployment, operation, scaling, and retirement. Useful controls include mandatory cost and ownership tags, scheduled shutdown of non-production resources, rightsizing analysis, retention limits, and policy checks for unnecessary duplication. Environmental impact cannot be inferred solely from cloud cost, because price and energy use are related but not identical measures.

A responsible organization therefore treats sustainability as a design constraint and a reporting requirement. Teams should be able to explain why resources exist, who owns them, how long they are expected to live, and what evidence supports their selected capacity.

AI-Assisted IaC and Autonomous Remediation

Generative AI can assist with Terraform resources, Ansible tasks, documentation, test generation, error interpretation, and migration analysis. Its value is highest when it reduces routine work while leaving architectural judgment and accountability with qualified people.

AI-generated infrastructure code must be treated as an untrusted draft. Models may invent resource arguments, use insecure defaults, misunderstand provider versions, omit dependencies, or generate code that appears plausible but does not represent the organization’s policy. AI tools may also expose confidential configuration or proprietary code if prompts and integrations are not governed.

A safe workflow places AI assistance inside existing controls: approved tools, restricted data, version control, static analysis, policy checks, testing, peer review, and controlled deployment. Provenance should be recorded where necessary, particularly for regulated or safety-sensitive changes.

Autonomous remediation extends the idea by allowing a system to detect and correct an issue without human approval. It is appropriate for low-risk, reversible, well-understood conditions, such as restarting a failed stateless process or applying a known configuration correction. It is inappropriate to grant unrestricted authority to delete resources, change identity policies, or modify production data based on uncertain signals.

A useful autonomy classification is:

  • advisory: the system recommends an action;
  • assisted: the system prepares a change for human approval;
  • bounded autonomous: the system acts within explicit limits and rollback conditions;
  • fully autonomous: the system chooses and executes changes with minimal intervention.

Most enterprise environments should expand autonomy gradually, using evidence from incidents, false positives, rollback performance, and business impact.

Intent-Based Infrastructure and Evolving Provisioning

Traditional IaC describes desired resources through declarative configuration. Intent-based infrastructure moves one level higher by expressing an outcome, such as “provide a highly available service with defined recovery objectives and a maximum cost,” while an engine selects implementation details. This approach resembles policy-driven and model-driven management.

Its promise is reduced cognitive load and greater consistency. Its difficulty is that intent must be precise enough to evaluate. “Secure and scalable” is not an executable intent unless the organization defines security conditions, scale thresholds, availability targets, data locations, and acceptable trade-offs.

Future provisioning systems are likely to combine declarative configuration, policy engines, event-driven operations, telemetry, optimization algorithms, and AI agents. The boundary between provisioning and operations may become less distinct: systems will not only create infrastructure but continuously compare observed state with organizational intent.

This development increases the importance of explainability and control. Enterprises will need to know which policy led to a change, what alternatives were rejected, what data informed the decision, and how the action can be reversed. More autonomous infrastructure requires more disciplined governance, not less.

Illustrative Case: Northstar Retail’s IaC Transformation

Background

Northstar Retail is a fictional multinational retailer operating customer-facing web applications, warehouse systems, and analytics platforms across several regions. Development teams provision cloud resources independently, while a central operations group manages production. The company uses Terraform for cloud resources and Ansible for server configuration, but practices vary substantially between teams.

Situation and Problem

Environment creation can take several weeks because requests pass through multiple manual queues. Production incidents reveal configuration drift between regions. Security audits identify inconsistent identity permissions and incomplete ownership tags. Finance also reports a large number of idle development resources. A proposal is made to standardize all infrastructure immediately through a central Terraform team.

Analysis

The proposed central model would improve consistency but would create a bottleneck and fail to address application-team ownership. Interviews reveal that teams lack reusable patterns, testing knowledge, and clear boundaries. The organization also depends on several provider-specific managed services; attempting to remove all lock-in would delay the transformation without producing proportional value.

Northstar establishes baseline measures for provisioning lead time, drift, failed changes, recovery time, resource ownership, and non-production cost. It selects a low-risk but representative pilot: a standardized application environment used by three development teams.

Approach and Strategy

Northstar adopts a hybrid platform model. A central enablement group owns approved modules, pipeline templates, security policies, and training. Product teams own their application stacks and contribute improvements through a review process. Network connectivity, identity boundaries, and regulated data services remain subject to central architecture controls.

The platform introduces versioned Terraform modules, Ansible roles for hardened hosts, remote state with locking and access control, mandatory tagging, secret-management integration, and policy checks. Routine development changes can proceed through automated validation, while production identity and network changes require risk-based approval.

To address sustainability and cost, temporary environments receive expiration metadata and scheduled shutdown policies. Exceptions are recorded with an owner, justification, expiry date, and compensating control. An AI coding assistant is permitted only within an approved enterprise environment; generated code must pass the same tests and reviews as human-written code.

Outcome

The pilot demonstrates faster environment creation, more consistent configuration, and improved visibility of resource ownership. The organization does not claim that every result is caused solely by Terraform or Ansible; it attributes improvement to the combined effect of standard modules, pipeline controls, training, and clearer accountability. After the pilot, Northstar expands the platform gradually and retains provider-specific services where they create demonstrable business value.

Learning Points

The case illustrates that enterprise IaC is an operating-model decision. Central standards and federated ownership can coexist. Metrics must include reliability, governance, cost, and delivery outcomes. AI assistance is valuable only when embedded in established controls. Finally, portability and sustainability should be managed as explicit design considerations rather than afterthoughts.

Risks, Limitations, and Common Mistakes

A frequent mistake is beginning with tool selection instead of operating-model design. Another is migrating manual procedures into code without simplifying them. This produces automated complexity rather than improved capability.

Other risks include unmanaged state, excessive privileges in pipelines, secrets committed to repositories, untested modules, uncontrolled module dependencies, insufficient rollback planning, and failure to retire obsolete resources. Drift detection can identify differences, but automatically overwriting manual changes may destroy legitimate emergency fixes. Teams should establish how drift is classified, investigated, and reconciled.

IaC also has limits. It cannot by itself resolve unclear ownership, poor architecture, inadequate monitoring, or conflicting business priorities. Declarative code may describe the intended state without proving that the service is healthy or that users are receiving acceptable performance. Infrastructure tests can reduce risk but cannot eliminate uncertainty in distributed systems.

Best-Practice Principles

A durable enterprise approach can be summarized through the following principles:

1. Begin with measurable business and operational problems.

2. Treat IaC as a capability and operating model, not merely a tool.

3. Establish ownership, decision rights, and exception processes early.

4. Prefer secure, tested, versioned reusable patterns over duplicated configuration.

5. Place routine governance controls in automated pipelines.

6. Keep human review for high-impact, uncertain, or irreversible decisions.

7. Measure outcomes such as reliability, recovery, cost, and delivery speed.

8. Design for appropriate portability rather than theoretical independence from all vendors.

9. Make resource lifecycle, sustainability, and retirement explicit.

10. Treat AI-generated code and autonomous actions as risk-bearing engineering outputs.

Chapter Summary

Enterprise IaC is a strategic capability that combines declarative configuration with governance, operating models, skills, platforms, and accountability. Successful adoption usually progresses from baseline assessment and bounded pilots to reusable standards, platform services, and policy-driven self-service. Centralized, federated, and platform-oriented models each provide different balances of consistency, autonomy, and responsiveness.

A sound business case considers efficiency, risk avoidance, resilience, auditability, and strategic enablement rather than labor savings alone. Governance should be automated where possible and risk-based where human judgment is required. Vendor lock-in should be assessed deliberately, while sustainability requires visibility, lifecycle controls, and capacity discipline.

Ethical IaC demands responsibility, transparency, proportionality, and contestability. AI-assisted coding and autonomous remediation can improve productivity, but generated or automated changes require testing, policy enforcement, review, and bounded authority. Intent-based infrastructure may raise the level of abstraction from resource declaration to business outcomes, making explainability and reversible control increasingly important.

Key Terms

IaC: The practice of defining and managing infrastructure through machine-readable, version-controlled specifications.

Operating model: The arrangement of people, processes, technology, and decision rights through which work is performed.

Platform team: A team that provides reusable internal technology capabilities as products for consuming teams.

Policy-as-code: Machine-readable rules that automatically evaluate or enforce governance and security requirements.

Configuration drift: A difference between declared infrastructure state and the state actually deployed or observed.

Remote state: Centrally stored IaC state used for collaboration, locking, backup, and controlled access.

Risk-based approval: Approval whose level of scrutiny depends on the potential impact and reversibility of a change.

Vendor lock-in: Dependence on a provider, tool, or ecosystem that makes replacement difficult or costly.

Portability: The ability to move workloads, configurations, or data between platforms with limited redesign.

Paved road: An approved, supported, and relatively easy path for delivering a common technical capability.

Autonomous remediation: Automated detection and correction of an operational condition without direct human execution.

Intent-based infrastructure: Infrastructure management in which desired outcomes or constraints guide implementation choices.

Contestability: The ability to question, review, appeal, or reverse an automated decision.

Provenance: Information about the origin, history, tools, and transformations associated with an artifact or change.

Sustainability control: A policy or practice that limits unnecessary resource use and supports environmental responsibility.

Exception management: The formal process for authorizing, recording, reviewing, and expiring deviations from standards.

Infrastructure industrialization: The standardization, measurement, governance, and continuous improvement of infrastructure operations.

Residual risk: The risk remaining after controls and mitigation measures have been applied.

Review Questions

Short Answer Questions

1. Why should IaC be treated as an enterprise operating capability rather than only a provisioning tool?

2. Distinguish between a centralized and a federated IaC operating model.

3. What is the purpose of policy-as-code in an IaC pipeline?

4. Identify two risks associated with AI-generated infrastructure code.

5. What does contestability mean in the context of ethical automation?

Long Answer Questions

1. Explain the stages of an enterprise IaC adoption roadmap and the measures that can be used to evaluate progress.

2. Discuss how an organization can design standards and governance controls without turning the IaC function into a delivery bottleneck.

3. Analyze the financial and strategic dimensions of an IaC business case.

4. Examine vendor lock-in and portability as strategic decisions rather than purely technical problems.

5. Discuss the ethical and accountability requirements for autonomous infrastructure remediation.

Analytical / Application Questions

1. An enterprise has independent Terraform repositories, inconsistent Ansible practices, rising cloud costs, and frequent production drift. Recommend an operating model and a two-year adoption strategy, explaining your decision rights and control mechanisms.

2. A proposed AI agent can automatically remove resources that violate cost policy. Evaluate the proposal using risk, reversibility, accountability, and contestability criteria, and design safer operating boundaries.

3. A company wants to become fully multi-cloud to avoid vendor lock-in. Using the concepts in this chapter, assess the proposal and recommend what should be abstracted, what may remain provider-specific, and how the decision should be reviewed over time.

22. Multiple Choice Questions with Answers

Question 1

Which statement best describes the difference between IaC automation and infrastructure industrialization?

A. Automation uses cloud services, whereas industrialization uses on-premises systems.

B. Automation performs tasks automatically, whereas industrialization also standardizes, measures, governs, and improves them.

C. Automation is declarative, whereas industrialization is always imperative.

D. Automation is used by developers, whereas industrialization is used only by auditors.

Correct Answer: B. Automation performs tasks automatically, whereas industrialization also standardizes, measures, governs, and improves them.

Question 2

Which operating model gives product teams ownership while a central group provides standards, enablement, and guardrails?

A. Fully centralized model

B. Unmanaged model

C. Federated model

D. Manual operations model

Correct Answer: C. Federated model

Question 3

Which control is most appropriate for protecting Terraform remote state?

A. Storing state in an unrestricted public repository

B. Disabling state locking to increase speed

C. Using access control, encryption, locking, and backup

D. Allowing every engineer to edit state directly

Correct Answer: C. Using access control, encryption, locking, and backup

Question 4

Why should an IaC business case include enablement benefits?

A. They demonstrate that all engineers can be replaced.

B. They capture new organizational capacity, such as launching services in additional regions.

C. They eliminate the need for baseline measurements.

D. They prove that vendor lock-in has been removed.

Correct Answer: B. They capture new organizational capacity, such as launching services in additional regions.

Question 5

Which situation is the strongest candidate for bounded autonomous remediation?

A. Deleting a production database after an uncertain anomaly

B. Changing identity boundaries without approval

C. Restarting a failed stateless process under defined conditions with rollback controls

D. Moving regulated data to another region automatically

Correct Answer: C. Restarting a failed stateless process under defined conditions with rollback controls

Question 6

What is a central ethical requirement for significant automated infrastructure decisions?

A. The decision must always be made by an AI model.

B. The decision must be irreversible to prevent human interference.

C. There must be identifiable responsibility and a means of review or reversal.

D. The decision must optimize cost above all other objectives.

Correct Answer: C. There must be identifiable responsibility and a means of review or reversal.

Question 7

Which approach best addresses vendor lock-in strategically?

A. Avoid every provider-specific service regardless of business value.

B. Use deliberate portability measures and assess exit costs against the value of specialization.

C. Use several cloud providers for every workload.

D. Rely on undocumented migration procedures until an exit is required.

Correct Answer: B. Use deliberate portability measures and assess exit costs against the value of specialization.

Question 8

Why can mandatory expiration metadata support sustainability?

A. It guarantees that all workloads use renewable energy.

B. It identifies temporary resources that can be reviewed or shut down when no longer needed.

C. It removes the need for capacity planning.

D. It prevents all production scaling.

Correct Answer: B. It identifies temporary resources that can be reviewed or shut down when no longer needed.

Question 9

How should AI-generated Terraform or Ansible code normally be treated?

A. As trusted production code because it was generated automatically

B. As an untrusted draft subject to testing, scanning, review, and policy controls

C. As documentation rather than executable material

D. As exempt from normal change-management procedures

Correct Answer: B. As an untrusted draft subject to testing, scanning, review, and policy controls

Question 10

What is the principal challenge of intent-based infrastructure?

A. It cannot express any desired state.

B. It requires precise, testable definitions of outcomes, constraints, and trade-offs.

C. It eliminates the need for monitoring.

D. It works only with manually configured servers.

Correct Answer: B. It requires precise, testable definitions of outcomes, constraints, and trade-offs.

Infrastructure as Code (IaC) with Terraform/Ansible
Chapter 14 of 14