Cloud Integration & Deployment (Heroku/AWS)

Chapter 9
Infrastructure as Code and Cloud Automation

Bachelors Program in Full Stack Development
Cloud Integration & Deployment (Heroku/AWS)   •   Bachelor

Infrastructure as Code and Cloud Automation

Introduction and Context

Cloud applications are rarely operated as isolated servers. A production system may include networks, identity policies, databases, queues, object storage, monitoring, secrets, load balancers, and application services. Creating these resources manually through a web console is slow, difficult to audit, and vulnerable to inconsistency. Infrastructure as Code (IaC) addresses this problem by describing infrastructure in machine-readable files that can be reviewed, tested, versioned, and applied repeatedly.

IaC is therefore more than automation. It is an operational model in which infrastructure becomes a controlled software artifact. A change to a security group can be reviewed like a source-code change; a complete test environment can be recreated from a repository; and an unwanted configuration difference can be detected and corrected.

This chapter focuses on declarative IaC and cloud automation using Terraform, AWS CloudFormation, and the AWS Cloud Development Kit (CDK). The examples use AWS terminology, but the principles also apply to Heroku pipelines, container platforms, and other cloud providers.

Learning Objectives

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

  • explain declarative infrastructure and compare it with imperative scripting;
  • describe the roles of Terraform, CloudFormation, and CDK;
  • design reusable modules and environment configurations;
  • explain state management, planning, locking, and secrets protection;
  • identify and remediate configuration drift;
  • apply policy as code, immutable infrastructure, and automated lifecycle management;
  • construct a safe CI/CD workflow for infrastructure changes; and
  • evaluate technical, financial, security, and governance trade-offs.

The IaC Mental Model

Imperative and declarative approaches

An imperative script specifies the sequence of operations: create a network, then create a subnet, then launch a server. The script must handle ordering, retries, partial failure, and the possibility that a resource already exists.

A declarative configuration specifies the desired end state: a network with particular address ranges, subnets, and routing rules. The IaC engine compares that desired state with its knowledge of the current state and determines the actions needed to converge the environment.

The desired-state model can be expressed conceptually as:

D = f(C, P, V)

Here, D is the desired infrastructure state, C is the configuration, P represents provider behavior and platform defaults, and V represents variable values. The expression is not a guarantee that the result is deterministic in every operational detail: cloud APIs may assign identifiers, IP addresses, or timestamps dynamically. It means that the configuration and inputs define the intended outcome.

A reconciliation engine attempts to move actual state A toward desired state D. In practice, the action set depends on the provider and tool:

Actions = reconcile(D, A, S)

where S is the tool's recorded state. This model assumes that resources are identifiable, the provider API is available, and the configuration is valid. It does not eliminate failures; it makes them visible and repeatable.

Idempotence and convergence

An operation is idempotent when applying it repeatedly produces the same intended result as applying it once. IaC tools seek idempotent behavior, although provider APIs and custom scripts can introduce non-idempotent side effects. For example, declaring an S3 bucket is usually idempotent, while a script that creates a new database snapshot every time it runs is not.

Convergence means that repeated successful executions approach the declared state. Convergence can be interrupted by permission errors, quota limits, dependency failures, or external changes. Consequently, a plan must be reviewed and an apply operation must be observable.

Major IaC Tools

ToolPrimary modelStrengthsImportant considerations
AWS CloudFormationAWS-native declarative templatesDeep AWS integration, stack lifecycle, change setsPrimarily AWS-focused; templates can become verbose
TerraformDeclarative configuration with providersMulti-cloud support, mature module ecosystem, explicit planState must be secured and coordinated
AWS CDKGeneral-purpose languages compiled to CloudFormationAbstraction, reusable constructs, familiar programming languagesAbstraction can hide generated resources; synthesis must be inspected

CloudFormation

CloudFormation templates commonly use YAML or JSON. A stack is a managed collection of resources. CloudFormation tracks dependencies, supports change sets, and can roll back failed operations. Parameters, mappings, conditions, outputs, and nested stacks support reuse.

A simplified resource declaration might look like this:

Resources:
  AppBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

The declaration expresses properties, not a sequence of console clicks. However, CloudFormation cannot make an unsafe design safe automatically. Public access, deletion policies, encryption, and retention behavior must be deliberately specified.

Terraform

Terraform configurations use HashiCorp Configuration Language (HCL). Providers translate resource declarations into API operations. A minimal example is:

resource "aws_s3_bucket" "app" {
  bucket = var.bucket_name
}

resource "aws_s3_bucket_public_access_block" "app" {
  bucket                  = aws_s3_bucket.app.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

Terraform constructs a dependency graph from references such as aws_s3_bucket.app.id. The usual workflow is terraform init, terraform validate, terraform plan, and terraform apply. A plan is an important control: it exposes additions, modifications, and destructions before they occur.

AWS CDK

CDK permits developers to define cloud infrastructure in languages such as TypeScript, Python, Java, or C#. It synthesizes CloudFormation templates. Constructs provide abstraction levels: low-level resource constructs closely represent CloudFormation resources, while higher-level constructs configure common patterns.

For example, a TypeScript application can define an encrypted bucket using a construct and then run cdk synth to inspect the generated template. CDK improves reuse and testing, but generated output remains the deployable contract. Developers must understand logical IDs, replacement behavior, permissions, and synthesized resources rather than treating CDK as magic.

Configuration Structure and Reusable Modules

A maintainable repository normally separates application code from infrastructure or clearly defines their relationship. A typical layout is:

infrastructure/
  modules/
    network/
    application/
    database/
  environments/
    development/
    staging/
    production/
  policies/
  tests/

A module is a reusable unit with documented inputs, outputs, resource behavior, and assumptions. Good module boundaries represent a coherent capability, such as a network or a web service, rather than arbitrary collections of resources.

Inputs should be constrained and validated. For example, an environment variable should accept only known values when the module has environment-specific behavior. Outputs should expose only what consumers need. A module should avoid embedding credentials, account identifiers, or unexplained production defaults.

Environment separation may use separate accounts, subscriptions, projects, or state files. Separate cloud accounts generally provide stronger isolation than a single account with naming conventions. At minimum, development and production should have distinct credentials, approval paths, and data controls.

State Management

Declarative configuration describes intent, but the tool must associate declarations with real resources. Terraform uses a state file to record resource identities, attributes, and dependencies. CloudFormation stores stack state through AWS services. State is operationally sensitive because it can contain resource metadata and, depending on configuration and provider behavior, secret values.

A production Terraform backend should normally provide:

  • remote storage with encryption at rest;
  • versioning and recovery capability;
  • access control based on least privilege;
  • state locking or an equivalent concurrency mechanism; and
  • audit logging.

Two engineers applying different plans against the same state can cause race conditions. Locking prevents simultaneous state mutation, but it does not replace code review. A failed lock, stale plan, or interrupted apply should be investigated rather than bypassed casually.

Never commit state files, private keys, database passwords, or provider credentials to a public repository. Secret values should be stored in a managed secret service or injected through a controlled CI/CD identity. Marking a variable as sensitive may hide it in terminal output, but it does not necessarily remove it from state.

Planning, Application, and Change Safety

A robust workflow separates design from execution:

  1. A developer changes version-controlled configuration.
  2. Formatting, syntax checks, static analysis, and policy tests run automatically.
  3. The tool creates a plan against the target environment.
  4. Reviewers examine the plan, especially replacements and deletions.
  5. An authorized identity applies the approved plan.
  6. Outputs, logs, health checks, and costs are recorded.

A plan can become stale if resources change between planning and applying. High-assurance pipelines therefore generate a plan and apply that exact reviewed artifact where the tool supports it. Production access should use short-lived federated credentials rather than long-lived access keys.

Replacement deserves special attention. Some property changes can occur in place; others require destroying and recreating a resource. Replacement can cause downtime, data loss, changed identifiers, or a new address. Database resources should have explicit backup, retention, and deletion-protection strategies. A prevent_destroy control can reduce accidental deletion, but it can also obstruct legitimate lifecycle operations and must be governed.

Drift Detection and Reconciliation

Drift is a difference between declared or recorded configuration and the actual cloud environment. It may arise from a console edit, an emergency change, an automatic platform update, a failed deployment, or an untracked resource.

Terraform commonly detects differences during refresh and planning. CloudFormation provides stack drift detection for supported resources. Drift detection is not identical to security monitoring: a resource may match the declared template while still violating a security policy, and some provider attributes may be unsupported or difficult to compare.

A sound response is:

  1. identify the changed resource and attribute;
  2. determine whether the change was authorized;
  3. either import and document the resource, update code, or revert the change;
  4. investigate the cause; and
  5. improve permissions or process if unauthorized changes recur.

Blindly overwriting drift can destroy an emergency fix or valuable data. Conversely, accepting every manual change defeats reproducibility. Organizations should define an exception process with an expiry date and a follow-up code change.

Policy as Code

Policy as code expresses organizational rules in machine-evaluable form. Examples include:

  • storage must not be publicly readable;
  • databases must use encryption and backups;
  • resources must contain owner and cost-center tags;
  • production changes require an approved pipeline;
  • IAM policies must not grant unrestricted administrative access;
  • permitted regions must be explicitly listed.

Policies can run before deployment, during plan review, or continuously against the deployed environment. Tools and approaches include Terraform policy frameworks, CloudFormation Guard, Open Policy Agent, cloud-native policy services, and static analyzers. Policy design should distinguish a hard security prohibition from a warning or recommended practice. Excessive false positives encourage teams to bypass controls.

Policy as code does not remove human judgment. A rule requiring encryption is clear; a rule defining acceptable data residency may require legal and business interpretation. Policy exceptions should be documented, time-limited, approved, and auditable.

Immutable Infrastructure and Lifecycle Automation

Immutable infrastructure replaces rather than modifies deployed instances. A new machine image or container artifact is built, tested, and deployed; old instances are retired after the new version becomes healthy. This reduces configuration drift inside machines and improves rollback, but it requires reliable image pipelines, externalized state, health checks, and sufficient capacity for overlap.

Mutable infrastructure can be appropriate for stateful systems, emergency remediation, or platforms where replacement is costly. The key issue is controlled change, not an absolute rule that mutation is always wrong.

A common lifecycle pattern is:

  • Build: create a versioned image or container.
  • Test: scan dependencies, test startup, and validate policy.
  • Deploy: launch a new environment or capacity group.
  • Verify: use health checks, metrics, logs, and smoke tests.
  • Shift traffic: use a rolling, blue-green, or canary strategy.
  • Retire: remove the old version after the rollback window.

Automated environment lifecycle management should also address temporary environments. Pull-request environments can be created for integration testing and destroyed after review. Time-to-live tags, scheduled cleanup, budget alarms, and ownership metadata prevent abandoned resources from generating cost and security exposure.

Worked Example: A Safe Environment Change

Suppose a team changes an application environment from a single private subnet to two private subnets across availability zones. The configuration change should not simply be applied from a laptop.

First, the module input is changed and validated. The plan is generated for the development environment. Reviewers check that the route tables, NAT design, security groups, and subnet associations are correct. They verify that the plan does not replace the database or expose a public address. Automated policy checks confirm required tags and encryption. After deployment, integration tests verify that the application can reach its database and that inbound access remains restricted.

The same module is then promoted to staging with different approved variables. Production uses a separate state boundary and requires an approval. If the plan proposes database replacement, deployment stops until the team designs a migration or explicitly authorizes the operation.

This example illustrates an important principle: IaC automates execution, not responsibility. Human review remains necessary for destructive, security-sensitive, or business-critical changes.

Case Study: Illustrative Online Learning Platform

Background

An illustrative university learning platform consists of a web front end, an API, a relational database, object storage for assignments, and a queue for background processing. Initially, developers created resources manually. Development and production differed, deployments were difficult to reproduce, and a storage bucket was accidentally made publicly readable.

Problem

The institution needed repeatable environments, auditable changes, protection of student records, and a way to create short-lived environments for feature branches without leaving resources running indefinitely.

Analysis

The team identified four risks: undocumented configuration, excessive console privileges, insecure defaults, and lack of ownership for temporary environments. It also recognized that state containing identifiers and configuration data required stronger protection than an ordinary source file.

Approach and Solution

The team selected Terraform for infrastructure definitions and a remote, encrypted, versioned backend with locking. Network, application, database, and storage resources were separated into modules. Development, staging, and production used separate state boundaries and cloud identities.

The CI pipeline ran formatting, validation, security scanning, policy tests, and plan generation. A policy prohibited public object storage, required encryption and backup settings, and required owner, environment, and cost-center tags. Production applied only an approved plan. Database deletion protection and backup retention were configured explicitly.

Application environments were deployed immutably using versioned container images. Pull-request environments received an expiry tag and were removed by a scheduled cleanup job after the review period. Monitoring detected drift and generated a ticket rather than automatically destroying unexpected resources.

Outcome

The platform could recreate a test environment consistently, and reviewers could see infrastructure changes before deployment. The public-storage incident became less likely because the policy failed the plan before application. The team also gained better cost attribution. However, the process required ongoing maintenance: modules had to be updated for provider changes, exceptions had to be reviewed, and engineers had to understand generated plans.

Learning Points

The case demonstrates that IaC is a socio-technical control. Tool selection matters, but identity design, repository governance, review discipline, observability, and lifecycle ownership determine whether automation improves reliability.

Professional, Ethical, and Governance Considerations

Infrastructure decisions affect confidentiality, availability, accessibility, and financial resources. Least privilege should apply to developers, CI runners, and automation roles. Production credentials should be separated from development credentials, and logs should avoid exposing secrets or personal data.

Regulated workloads may require evidence of approval, change history, data residency, retention, backup testing, and incident response. IaC repositories and plan artifacts can become audit evidence, but they may also reveal architecture and sensitive identifiers. Access should be controlled and retention should follow organizational policy.

Automation can amplify mistakes. A faulty module may deploy an insecure configuration across many accounts. Therefore, blast-radius reduction is essential: use sandbox accounts, staged rollouts, policy gates, quotas, approval boundaries, and tested modules. Teams should also ensure that automated cleanup cannot delete data protected by retention requirements.

Contemporary Developments and Best Practices

Modern cloud engineering increasingly combines IaC with GitOps, platform engineering, software supply-chain security, and automated compliance. Infrastructure repositories may use pull requests as the principal change interface, while controllers reconcile approved declarations. Providers and modules are pinned and updated through controlled dependency management. Generated plans, images, and providers can be signed or verified where organizational capability supports it.

AI-assisted infrastructure generation can accelerate boilerplate creation, but generated configurations may contain excessive permissions, insecure defaults, incorrect dependencies, or fabricated provider arguments. Human review, automated validation, policy checks, and least-privilege testing remain mandatory. AI should assist reasoning, not become an unaccountable deployment authority.

Recommended practices include:

  • keep all production infrastructure in version control;
  • use modules with clear interfaces and documentation;
  • pin and regularly update providers and action dependencies;
  • protect remote state and enable locking;
  • review plans, especially replacements and deletions;
  • test policies before deployment and monitor after deployment;
  • use separate identities and state boundaries for environments;
  • prefer short-lived credentials and federated access;
  • tag resources for ownership, environment, and cost;
  • design rollback, backup, and disaster-recovery procedures; and
  • measure drift, failed deployments, lead time, recovery time, and cloud cost.

Challenges and Limitations

IaC introduces its own complexity. Provider behavior can change, abstractions can obscure resources, and state corruption can interrupt delivery. Large monolithic configurations create slow plans and broad blast radius. Overly generic modules become difficult to understand; overly specialized modules duplicate logic.

Declarative tools also cannot fully represent operational intent. They may declare a database, but not guarantee that a migration is backward compatible. They can create a queue, but not prove that consumers handle poison messages. Infrastructure tests must therefore include unit or synthesis tests, static policy checks, integration tests, deployment tests, and runtime observability.

Finally, reproducibility has boundaries. Cloud services evolve, external data changes, quotas vary, and some resources contain generated values. Pinning versions, recording inputs, using isolated test accounts, and documenting provider assumptions improve reproducibility without promising impossible exact identity.

Chapter Summary

Infrastructure as Code represents cloud resources as versioned, reviewable, machine-readable declarations. Declarative tools seek convergence between desired, recorded, and actual state, while plans expose proposed changes before execution. Terraform provides provider-based multi-cloud configuration; CloudFormation offers AWS-native stack management; and CDK supplies programmable abstractions that synthesize CloudFormation.

Reliable IaC depends on modular design, protected state, locking, separated environments, policy as code, drift detection, and controlled lifecycle automation. Immutable infrastructure can reduce configuration drift, while temporary environments and automated cleanup improve development speed and cost control. These benefits require governance: least privilege, approval workflows, auditability, backups, policy exceptions, and careful treatment of secrets. IaC is consequently both a technical practice and an organizational method for making infrastructure reproducible, observable, and accountable.

Key Terms

Infrastructure as Code: Managing infrastructure through machine-readable, version-controlled definitions.

Declarative configuration: A description of the desired end state rather than a procedural sequence.

Imperative automation: Automation that specifies the operations and order of execution.

Idempotence: The property that repeated application produces the same intended result as one application.

Convergence: Movement of actual infrastructure toward declared desired state.

Terraform: A declarative IaC tool using providers and state.

CloudFormation: AWS-native service for provisioning and managing resource stacks.

AWS CDK: A programming-language framework that synthesizes CloudFormation templates.

Module: A reusable, documented unit of infrastructure configuration.

State: Recorded information linking configuration with deployed resources.

State locking: A control that prevents concurrent modification of shared state.

Drift: A difference between declared or recorded configuration and actual resources.

Policy as code: Machine-evaluable rules governing infrastructure or deployments.

Immutable infrastructure: Infrastructure replaced with new versions rather than modified in place.

Change set: A preview of CloudFormation modifications before execution.

Remote backend: Centrally managed storage for IaC state.

Least privilege: Granting only the permissions necessary for a task.

Blue-green deployment: Running old and new environments concurrently and shifting traffic between them.

GitOps: Managing operational state through version-controlled declarations and reconciliation.

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

Review Questions

Short Answer Questions

  1. What is the difference between declarative and imperative infrastructure automation?
  2. Why must Terraform state be protected and locked?
  3. Define infrastructure drift and give two possible causes.
  4. What is policy as code, and how can it prevent an insecure deployment?
  5. Why can immutable infrastructure improve operational consistency?

Long Answer Questions

  1. Compare Terraform, CloudFormation, and AWS CDK in terms of abstraction, portability, state, and operational risk.
  2. Explain a complete CI/CD workflow for a production infrastructure change, including validation, planning, approval, application, and verification.
  3. Discuss the design of reusable modules and environment boundaries for a multi-environment web application.
  4. Explain how drift detection should be combined with incident response and governance rather than treated as an automatic overwrite mechanism.
  5. Evaluate the security, reliability, and cost implications of temporary pull-request environments.

Analytical / Application Questions

  1. A Terraform plan proposes replacing a production database after a schema-related configuration change. Analyze the risks and propose a safe response.
  2. An organization has frequent console changes that create drift. Design a governance and technical intervention that preserves emergency-response capability while restoring reproducibility.
  3. Design an IaC architecture for an online service containing a web tier, private database, object storage, and background workers. Justify module boundaries, state separation, policy controls, deployment strategy, and cleanup mechanisms.

Multiple Choice Questions with Answers

Question 1

Which statement best describes declarative Infrastructure as Code?

A. It specifies the desired end state and lets the tool determine required actions.

B. It requires an operator to execute every cloud API call manually.

C. It stores only application source code and excludes infrastructure resources.

D. It always provisions resources in a fixed sequence without dependency analysis.

Correct Answer: A. It specifies the desired end state and lets the tool determine required actions.

Question 2

What is the primary purpose of Terraform state?

A. To store application user passwords in plain text.

B. To associate configuration declarations with real resource identities and attributes.

C. To replace all cloud-provider audit logs.

D. To compile TypeScript into CloudFormation templates.

Correct Answer: B. To associate configuration declarations with real resource identities and attributes.

Question 3

Which practice best protects a production Terraform backend?

A. Committing the state file to a public source repository.

B. Using a shared administrator access key on developer laptops.

C. Using encrypted remote storage, access controls, versioning, and locking.

D. Deleting the state file after every deployment.

Correct Answer: C. Using encrypted remote storage, access controls, versioning, and locking.

Question 4

What is infrastructure drift?

A. A planned change documented in a pull request.

B. A difference between declared or recorded configuration and actual resources.

C. A provider plugin used by Terraform.

D. A backup copy of a CloudFormation template.

Correct Answer: B. A difference between declared or recorded configuration and actual resources.

Question 5

Which control is an example of policy as code?

A. A developer informally reminding colleagues to add tags.

B. A rule that fails a plan when an object-storage bucket is publicly readable.

C. A manually edited diagram of the network.

D. A production deployment performed without review.

Correct Answer: B. A rule that fails a plan when an object-storage bucket is publicly readable.

Question 6

What is a principal benefit of immutable infrastructure?

A. It encourages undocumented manual changes to long-lived servers.

B. It reduces configuration drift by replacing instances with tested versions.

C. It eliminates the need for health checks and monitoring.

D. It guarantees that every cloud resource has an identical physical address.

Correct Answer: B. It reduces configuration drift by replacing instances with tested versions.

Question 7

What does AWS CDK primarily do before deployment?

A. It synthesizes infrastructure definitions into CloudFormation templates.

B. It converts databases into container images.

C. It disables AWS identity policies.

D. It permanently replaces the AWS console.

Correct Answer: A. It synthesizes infrastructure definitions into CloudFormation templates.

Question 8

Why should a Terraform plan be reviewed before apply?

A. To identify proposed additions, modifications, replacements, and deletions.

B. Because Terraform cannot validate configuration syntax.

C. To ensure that no infrastructure is represented in source control.

D. Because plans are unrelated to the target environment.

Correct Answer: A. To identify proposed additions, modifications, replacements, and deletions.

Question 9

Which design most effectively limits production blast radius?

A. One shared administrator identity and one state file for every environment.

B. Separate environment boundaries, least-privilege identities, policy gates, and staged rollout.

C. Allowing unrestricted console edits in production.

D. Disabling backups to accelerate every deployment.

Correct Answer: B. Separate environment boundaries, least-privilege identities, policy gates, and staged rollout.

Question 10

A team wants temporary environments for pull requests. Which practice most directly reduces abandoned-resource cost?

A. Remove all ownership tags from temporary resources.

B. Use expiry metadata and an automated cleanup process with safeguards.

C. Give temporary environments permanent production credentials.

D. Disable monitoring until the pull request is merged.

Correct Answer: B. Use expiry metadata and an automated cleanup process with safeguards.

Cloud Integration & Deployment (Heroku/AWS)
Chapter 9 of 14