Movatterモバイル変換


[0]ホーム

URL:


HashiConf 2025Don't miss the live stream of HashiConf Day 2 happening now View live stream

Refactor monolithic Terraform configuration

  • 18min
  • |
  • Terraform
  • Interactive

Some Terraform projects start as amonolith, a Terraform project managed by a single main configuration file in a single directory, with a single state file. Small projects may be convenient to maintain this way. However, as your infrastructure grows, restructuring your monolith into logical units will make your Terraform configurations less confusing and safer to manage.

These tutorials are for Terraform users who need to restructure Terraform configurations as they grow. In this tutorial, you will provision two instances of a web application hosted in an S3 bucket that represent production and development environments. The configuration you use to deploy the application will start in as a monolith. You will modify it to step through the common phases of evolution for a Terraform project, until each environment has its own independent configuration and state.

Prerequisites

Although the concepts in this tutorial apply to any module creation workflow,this tutorial uses Amazon Web Services (AWS) modules.

To follow this tutorial you will need:

Launch Terminal

This tutorial includes a free interactive command-line lab that lets you follow along on actual cloud infrastructure.

Apply a monolith configuration

In your terminal, clone the examplerepository. It contains the configuration used in this tutorial.

$ git clone https://github.com/hashicorp-education/learn-terraform-code-organization

Tip

Throughout this tutorial, you will have the option to check out branches that correspond to the version of Terraform configuration in that section. You can use this as a failsafe if your deployment is not working correctly, or to run the tutorial without making changes manually.

Navigate to the directory.

$ cd learn-terraform-code-organization

Your root directory contains four files and an "assets" folder. The root directory files compose the configuration as well as the inputs and outputs of your deployment.

  • main.tf - configures the resources that make up your infrastructure.
  • variables.tf- declares input variables for yourdev andprod environment prefixes, and the AWS region to deploy to.
  • terraform.tfvars.example- defines your region and environment prefixes.
  • outputs.tf- specifies the website endpoints for yourdev andprod buckets.
  • assets- houses your webapp HTML file.

In your text editor, open themain.tf file. The file consists of a few different resources:

  • Therandom_pet resource creates a string to be used as part of the unique name of your S3 bucket.

  • Twoaws_s3_bucket resources designatedprod anddev, which each create an S3 bucket. Notice that thebucket argument defines the S3 bucket name by interpolating the environment prefix and therandom_pet resource name.

  • Twoaws_s3_bucket_acl resources designatedprod anddev, which set apublic-read ACL for your buckets.

  • Twoaws_s3_bucket_website_configuration resources designatedprod anddev, which configure your buckets to host websites.

  • Twoaws_s3_bucket_policy resources designatedprod anddev, which allow anyone to read the objects in the corresponding bucket.

  • Twoaws_s3_object resources designatedprod anddev, which load the file in the localassets directory using thebuilt infile()function and upload it to your S3 buckets.

Terraform requires unique identifiers - in this caseprod ordev for eachs3 resource - to create separate resources of the same type.

Open theterraform.tfvars.example file in your repository and edit it with your own variable definitions. Change theregion to your nearest location in your text editor.

terraform.tfvars.example

region= "us-east-1"prod_prefix= "prod"dev_prefix= "dev"

Save your changes in your editor and rename the file toterraform.tfvars. Terraform automatically loads variable values from any files that end in.tfvars.

$ mv terraform.tfvars.example terraform.tfvars

In your terminal, initialize your Terraform project.

$ terraform init

Then, apply the configuration.

$ terraform apply

Accept the apply plan by enteringyes in your terminal to create the 5 resources.

Navigate to the web address from the Terraform output to display the deployment in a browser. Your directory now contains a state file,terraform.tfstate.

Separate configuration

Defining multiple environments in the samemain.tf file may become hard to manage as you add more resources. The HashiCorp Configuration Language (HCL), which is the language used to write Terraform configurations, is meant to be human-readable and supports using multiple configuration files to help organize your infrastructure.

You will organize your current configuration by separating the configurations into two separate files — one root module for each environment.To split the configuration, first make a copy ofmain.tf and name itdev.tf.

$ cp main.tf dev.tf

Rename themain.tf file toprod.tf.

$ mv main.tf prod.tf

You now have two identical files. Opendev.tf and remove any references to the production environment by deleting the resource blocks with theprod ID. Repeat the process forprod.tf by removing any resource blocks with thedev ID.

Tip

To fast-forward to this file separated configuration, checkout the branch in your example repository by runninggit checkout file-separation.

Your directory structure will look similar to the one below.

.├── README.md├── assets│   └── index.html├── dev.tf├── outputs.tf├── prod.tf├── terraform.tfstate├── terraform.tfvars└── variables.tf

Although your resources are organized in environment-specific files, yourvariables.tf andterraform.tfvars files contain the variable declarations and definitions for both environments.

Terraform loads all configuration files within a directory and appends them together, so any resources or providers with the same name in the same directory will cause a validation error. If you were to run aterraform command now, yourrandom_pet resource andprovider block would cause errors since they are duplicated across the two files.

Edit theprod.tf file by commenting out theterraform block, theprovider block, and therandom_pet resource. You can comment out the configuration by adding a/* at the beginning of the commented out block and a*/ at the end, as shown below.

prod.tf

+/* terraform {   required_providers {     aws = {       source = "hashicorp/aws"       version = "~> 4.0.0"     }     random = {       source  = "hashicorp/random"       version = "~> 3.1.0"     }   } } provider "aws" {   region = var.region } resource "random_pet" "petname" {   length    = 3   separator = "-" }+*/

With yourprod.tf shared resources commented out, your production environment will still inherit the value of therandom_pet resource in yourdev.tf file.

Simulate a hidden dependency

You may want your development and production environments to share bucket names, but the current configuration is particularly dangerous because of the hidden resource dependency built into it. Imagine that you want to test a random pet name with four words in development. Indev.tf, update yourrandom_pet resource'slength attribute to4.

dev.tf

resource "random_pet" "random" {  length= 4  separator= "-"}

You might think you are only updating the development environment because you only changeddev.tf, but remember, this value is referenced by bothprod anddev resources.

$ terraform apply

Enteryes when prompted to apply the changes.

Note that the operation updated all five of your resources by destroying and recreating them. In this scenario, you encountered a hidden resource dependency because both bucket names rely on the same resource.

Carefully review Terraform execution plans before applying them. If an operator does not carefully review the plan output or if CI/CD pipelines automatically apply changes, you may accidentally apply breaking changes to your resources.

Destroy your resources before moving on. Respond to the confirmation prompt with ayes.

$ terraform destroy

Separate states

The previous operation destroyed both the development and production environment resources. When working with monolithic configuration, you can use theterraform apply command with the-target flag to scope the resources to operate on, but that approach can be risky and is not a sustainable way to manage distinct environments. For safer operations, you need to separate your development and production state.

State separation signals more mature usage of Terraform; with additional maturity comes additional complexity. There are two primary methods to separate state between environments: directories and workspaces.

To separate environments with potential configuration differences, use a directory structure. Use workspaces for environments that do not greatly deviate from one another, to avoid duplicating your configurations. Try both methods in the tabs below to understand which will serve your infrastructure best.

By creating separate directories for each environment, you can shrink the blast radius of your Terraform operations and ensure you will only modify intended infrastructure. Terraform stores your state files on disk in their corresponding configuration directories. Terraform operates only on the state and configuration in the working directory by default.

Directory-separated environments rely on duplicate Terraform code. This may be useful if you want to test changes in a development environment before promoting them to production. However, the directory structure runs the risk of creating drift between the environments over time. If you want to reconfigure a project with a single state file into directory-separated states, you must perform advanced state operations to move the resources.

After reorganizing your environments into directories, your file structure should look like the one below.

.├── assets│   ├── index.html├── prod│   ├── main.tf│   ├── variables.tf│   ├── terraform.tfstate│   └── terraform.tfvars└── dev   ├── main.tf   ├── variables.tf   ├── terraform.tfstate   └── terraform.tfvars

Createprod anddev directories

Create directories namedprod anddev.

$ mkdir prod&& mkdir dev

Move thedev.tf file to thedev directory, and rename it tomain.tf.

$ mv dev.tf dev/main.tf

Copy thevariables.tf,terraform.tfvars, andoutputs.tf files to thedev directory

$ cp outputs.tf terraform.tfvars variables.tf dev/

Your environment directories are now one step removed from theassets folder where your webapp lives. Open thedev/main.tf file in your text editor and edit the file to reflect this change by editing the file path in thecontent argument of theaws_s3_object resource with a/.. before theassets subdirectory.

dev/main.tf

 resource "aws_s3_object" "dev" {   acl          = "public-read"   key          = "index.html"   bucket       = aws_s3_bucket.dev.id-  content      = file("${path.module}/assets/index.html")+  content      = file("${path.module}/../assets/index.html")   content_type = "text/html" }

You will need to remove the references to theprod environment from yourdev configuration files.

First, opendev/outputs.tf in your text editor and remove the reference to theprod environment.

dev/outputs.tf

-output "prod_website_endpoint" {-  value = "http://${aws_s3_bucket_website_configuration.prod.website_endpoint}/index.html"-}

Next, opendev/variables.tf and remove the reference to theprod environment.

dev/variables.tf

-variable "prod_prefix" {-  description = "This is the environment where your webapp is deployed. qa, prod, or dev"-}

Finally, opendev/terraform.tfvars and remove the reference to theprod environment.

dev/terraform.tfvars

 region      = "us-east-2"-prod_prefix = "prod" dev_prefix  = "dev"

Create aprod directory

Renameprod.tf tomain.tf and move it to your production directory.

$ mv prod.tf prod/main.tf

Move thevariables.tf,terraform.tfvars, andoutputs.tf files to theprod directory.

$ mv outputs.tf terraform.tfvars variables.tf prod/

Repeat the steps you took in thedev directory, and uncomment out therandom_pet andprovider blocks inmain.tf.

First, openprod/main.tf and edit it to reflect new directory structure by adding/.. to the file path in thecontent argument of theaws_s3_object, before theassets subdirectory.

Next, remove the references to thedev environment fromprod/variables.tf,prod/outputs.tf, andprod/terraform.tfvars.

Finally, uncommentterraform block, theprovider block, and therandom_pet resource inprod/main.tf.

prod/main.tf

-/* terraform {   required_providers {     aws = {       source = "hashicorp/aws"       version = "~> 4.0.0"     }     random = {       source  = "hashicorp/random"       version = "~> 3.1.0"     }   } } provider "aws" {   region = var.region } resource "random_pet" "petname" {   length    = 3   separator = "-" }-*/

Tip

To fast-forward to this configuration, rungit checkout directories.

Deploy environments

To deploy, change directories into your development environment.

$ cd dev

This directory is new to Terraform, so you must initialize it.

$ terraform init

Run an apply for the development environment and enteryes when prompted to accept the changes.

$ terraform apply

You now have only one output from this deployment. Check your website endpoint in a browser.

Repeat these steps for your production environment.

$ cd ../prod

This directory is new to Terraform, so you must initialize it first.

$ terraform init

Run your apply for your production environment and enteryes when prompted to accept the changes. Check your website endpoint in a browser.

$ terraform apply

Now your development and production environments are in separate directories, each with their own configuration files and state.

Destroy infrastructure

Before moving on to the second approach to environment separation, destroy both thedev andprod resources.

$ terraform destroy

To learn about another method of environment separation, navigate to the"Workspaces" tab.

Next steps

In this exercise, you learned how to restructure a monolithic Terraform configuration that managed multiple environments. You separated those environments by creating different directories or workspaces, and state files for each. To learn more about how to organize your configuration, review the following resources:

This tutorial also appears in:

  • 33 tutorials
    Terraform Associate (003) Tutorials
    Progress through these tutorials to prepare for the Terraform Associate (003) certification exam.
    • Terraform

[8]ページ先頭

©2009-2025 Movatter.jp