Skip to main content

Write Some Components

When you design cloud architectures with Atmos, you will first break them apart into pieces called components. Then, you will implement Terraform "root modules" for each of those components.

After we're done with this, we'll show you how you connect your components using stacks, so that everything comes together.

You will learn

  • Where to place your components
  • How to write a suitable Terraform root module to use as a component
  • How to make your components reusable and other considerations

Step 0: Decide what your component should do

Once we consider our infrastructure in terms of components, we need to determine the specific functions each component must perform.

We recommend following the Single Responsibility Principle (SRP). That is, design components to serve a single purpose, making them the smallest possible unit of infrastructure in a typical software development lifecycle (SDLC). Group pieces usually change together as one component and separate those that change independently (or seldom).

We discuss some of the best practices for components in our documentation.

In this case, since our goal is to retrieve the current weather, let's create a component that will do just that.

Step 0: Create a Directory for Your Component

The implementation for the component (e.g. HCL code) will be stored in a directory under the components/ folder corresponding to the toolchain used (e.g. terraform/). For Terraform, place your component in the components/terraform directory. Inside this directory, create a folder to store your component’s Terraform code, which serves as the root module. Since our component is a simple Terraform root module, we will create a directory called weather to store the Terraform code for our weather component.

mkdir -p components/terraform/weather

Step 0: Write a Terraform Root Module

In the following descriptions, you’ll see that everything is just plain Terraform (HCL) with nothing specific to Atmos. That’s intentional: we want to demonstrate that Atmos works seamlessly with plain Terraform. Atmos introduces conventions around how you use Terraform with its framework, which will become more evident in the subsequent lessons.

To write our Terraform root module, we’ll follow Terraform's standard conventions:

variables.tf
Defines variables
outputs.tf
Defines outputs
versions.tf
Specifies required provider versions
providers.tf
Specifies providers
main.tf
Implements main functionality

So, let’s start by defining our root module as usual.

Implement variables.tf

To make the best use of Atmos, ensure your root modules are highly reusable by accepting parameters, allowing them to be deployed multiple times without conflicts. This also usually means provisioning resources with unique names.

examples/quick-start-simple/components/terraform/weather/variables.tf


Implement main.tf

The main.tf file is where the main implementation of your component resides. This is where you define all the business logic for what you’re trying to achieve—the core functionality of your root module. If this file becomes too large or complex, you can break it into multiple files in a way that makes sense. However, sometimes that is also a red flag, indicating that the component is trying to do too much and should be broken down into smaller components.

In this example, we define a local variable that creates a URL using the variable inputs we receive. We also set up a data source to perform an HTTP request to that endpoint and retrieve the current weather. Additionally, we write this output to a file to demonstrate a stateful resource.

examples/quick-start-simple/components/terraform/weather/main.tf


How do we recommend ensuring unique resource names?

Cloud Posse's terraform-null-label module makes this easier by turning naming into a standardized, programmatically enforced convention. Setting the null label parameters ensures unique resource names and enforces consistency across your team and organization.

NOTE:
In our example, we will keep it simple to focus on the basics, so we will not use this module.

For the purpose of this quickstart, we’ll assume you have minimal experience with Terraform. Some of this information might be obvious to more experienced users, but we’re including it here to ensure a smooth learning experience for everyone.

Implement versions.tf

The versions.tf file is where provider pinning is typically defined. Provider pinning increases the stability of your components and ensures consistency between deployments in multiple environments.

examples/quick-start-simple/components/terraform/weather/versions.tf


Implement outputs.tf

The outputs.tf file is where, by convention in Terraform, you place any outputs you want to expose from your root module. Outputs are essential for passing state between your root modules and can be used in conjunction with remote state or the Atmos function to retrieve the state of any other component. In object-oriented parlance, think of your outputs as the “public” attributes of the module that are intended to be accessed by other modules. This convention helps maintain clarity and organization within your Terraform configurations.

examples/quick-start-simple/components/terraform/weather/outputs.tf


NOTE:
Technically outputs can be placed in any file, but the standard convention in Terraform is to place them usually in outputs.tf.

Ready to take the next step?

Now that you have a Terraform "root module", you can use it as a component in your Atmos stacks, and deploy it any number of times, tweaking the configuration as needed.

We will show you how to do that in the next step.