Terraform Backends
Backends define where Terraform stores its state data files.
Atmos supports all the backends supported by Terraform:
Local Backend
By default, Terraform will use a backend called local, which stores Terraform state on the local filesystem, locks that state using system APIs, and performs operations locally.
Terraform's local backend is designed for development and testing purposes and is generally not recommended for production use. There are several reasons why using the local backend in a production environment may not be suitable:
- Not Suitable for Collaboration: Local backend doesn't support easy state sharing.
- No Concurrency and Locking: Local backend lacks locking, leading to race conditions when multiple users modify the state.
- Lacks Durability and Backup: Local backend has no durability or backup. Machine failures can lead to data loss.
- Unsuitable for CI/CD: Local backend isn't ideal for CI/CD pipelines.
To address these concerns, it's recommended to use one of the supported remote backends, such as Amazon S3, Azure Storage, Google Cloud Storage, HashiCorp Consul, or Terraform Cloud, for production environments. Remote backends provide better scalability, collaboration support, and durability, making them more suitable for managing infrastructure at scale in production environments.
AWS S3 Backend
Terraform's S3 backend is a popular remote backend for storing Terraform state files in an Amazon Simple Storage Service (S3) bucket. Using S3 as a backend offers many advantages, particularly in production environments.
To configure Terraform to use an S3 backend, you typically provide the S3 bucket name and an optional key prefix in your Terraform configuration. Here's a simplified example:
In the example, terraform_locks
is a DynamoDB table used for state locking. DynamoDB is recommended for locking when using the S3 backend to ensure
safe concurrent access.
Once the S3 bucket and DynamoDB table are provisioned, you can start using them to store Terraform state for the Terraform components. There are two ways of doing this:
- Manually create
backend.tf
file in each component's folder with the following content:
- Configure Terraform S3 backend with Atmos to automatically generate a backend file for each Atmos component. This is the recommended way of configuring Terraform state backend since it offers many advantages and will save you from manually creating a backend configuration file for each component
Configuring Terraform S3 backend with Atmos consists of three steps:
- Set
auto_generate_backend_file
totrue
in theatmos.yaml
CLI config file in thecomponents.terraform
section:
- Configure the S3 backend in one of the
_defaults.yaml
manifests. You can configure it for the entire Organization, or per OU/tenant, or per region, or per account.
The _defaults.yaml
manifests contain the default settings for Organizations, Organizational Units and accounts.
To configure the S3 backend for the entire Organization, add the following config in stacks/orgs/acme/_defaults.yaml
:
- (This step is optional) For each component, you can add
workspace_key_prefix
similar to the following:
Note that this is optional. If you don’t add backend.s3.workspace_key_prefix
to the component manifest, the Atmos component name will be used
automatically (which is this example is vpc
). /
(slash) in the Atmos component name will be replaced with -
(dash).
We usually don’t specify workspace_key_prefix
for each component and let Atmos use the component name as workspace_key_prefix
.
Once all the above is configured, when you run the commands atmos terraform plan vpc -s <stack>
or atmos terraform apply vpc -s <stack>
, before executing the Terraform commands, Atmos will deep-merge
the backend configurations from the _defaults.yaml
manifest and from the component itself, and will generate a backend
config JSON file backend.tf.json
in the component's folder, similar to the following example:
You can also generate the backend configuration file for a component in a stack by executing the command atmos terraform generate backend. Or generate the backend configuration files for all components by executing the command atmos terraform generate backends.
Azure Blob Storage Backend
azurerm
backend stores the state as a
Blob with the given Key within the Blob Container within the Blob Storage Account. This backend supports state locking
and consistency checking with Azure Blob Storage native capabilities.
To configure the Azure Blob Storage backend
in Atmos, add the following config to an Atmos manifest in _defaults.yaml
:
For each component, you can optionally add the key
parameter similar to the following:
If the key
is not specified for a component, Atmos will use the component name (my-component
in the example above)
to auto-generate the key
parameter in the format <component-name>.terraform.tfstate
replacing <component-name>
with the Atmos component name. In <component-name>
, all occurrences of /
(slash) will be replaced with -
(dash).
If auto_generate_backend_file
is set to true
in the atmos.yaml
CLI config file in the components.terraform
section,
Atmos will deep-merge the backend configurations from the _defaults.yaml
manifests and
from the component itself, and will generate a backend config JSON file backend.tf.json
in the component's folder,
similar to the following example:
Google Cloud Storage Backend
gcs
backend stores the state as an object
in a configurable prefix
in a pre-existing bucket on Google Cloud Storage (GCS).
The bucket must exist prior to configuring the backend. The backend supports state locking.
To configure the Google Cloud Storage backend
in Atmos, add the following config to an Atmos manifest in _defaults.yaml
:
For each component, you can optionally add the prefix
parameter similar to the following:
If the prefix
is not specified for a component, Atmos will use the component name (my-component
in the example above)
to auto-generate the prefix
. In the component name, all occurrences of /
(slash) will be replaced with -
(dash).
If auto_generate_backend_file
is set to true
in the atmos.yaml
CLI config file in the components.terraform
section,
Atmos will deep-merge the backend configurations from the _defaults.yaml
manifests and
from the component itself, and will generate a backend config JSON file backend.tf.json
in the component's folder,
similar to the following example:
Terraform Cloud Backend
Terraform Cloud backend uses a cloud
block to specify
which organization and workspace(s) to use.
To configure the Terraform Cloud backend
in Atmos, add the following config to an Atmos manifest in _defaults.yaml
:
For each component, you can optionally specify the workspaces.name
parameter similar to the following:
If auto_generate_backend_file
is set to true
in the atmos.yaml
CLI config file in the components.terraform
section,
Atmos will deep-merge the backend configurations from the _defaults.yaml
manifests and
from the component itself, and will generate a backend config JSON file backend.tf.json
in the component's folder,
similar to the following example:
Instead of specifying the workspaces.name
parameter for each component in the component manifests, you can use
the {terraform_workspace}
token in the cloud
backend config in the _defaults.yaml
manifest.
The token {terraform_workspace}
will be automatically replaced by Atmos with the Terraform workspace for each component.
This will make the entire configuration DRY.
Refer to Terraform Workspaces in Atmos for more information on how Atmos calculates Terraform workspaces for components, and how workspaces can be overridden for each component.
Terraform Backend Inheritance
Suppose that for security and audit reasons, you want to use different Terraform backends for dev
, staging
and prod
.
Each account needs to have a separate S3 bucket, DynamoDB table, and IAM role with different permissions
(for example, the development
Team should be able to access the Terraform backend only in the dev
account, but not in staging
and prod
).
Atmos supports this use-case by using deep-merging of stack manifests, Imports and Inheritance, which makes the backend configuration reusable and DRY.
We'll split the backend config between the Organization and the accounts.
Add the following config to the Organization stack manifest in stacks/orgs/acme/_defaults.yaml
:
Add the following config to the dev
stack manifest in stacks/orgs/acme/plat/dev/_defaults.yaml
:
Add the following config to the staging
stack manifest in stacks/orgs/acme/plat/staging/_defaults.yaml
:
Add the following config to the prod
stack manifest in stacks/orgs/acme/plat/prod/_defaults.yaml
:
When you provision the vpc
component into the dev
account (by executing the command atmos terraform apply vpc -s plat-ue2-dev
), Atmos will
deep-merge the backend configuration from the Organization-level manifest with the configuration from the dev
manifest, and will automatically
add workspace_key_prefix
for the component, generating the following final deep-merged backend config for the vpc
component in the dev
account:
In the same way, you can create different Terraform backends per Organizational Unit, per region, per account (or a group of accounts, e.g. prod
and non-prod
), or even per component or a set of components (e.g. root-level components like account
and IAM roles can have a separate backend),
and then configure parts of the backend config in the corresponding Atmos stack manifests. Atmos will deep-merge all the parts from the
different scopes and generate the final backend config for the components in the stacks.
Terraform Backend with Multiple Component Instances
We mentioned before that you can configure the Terraform backend for the components manually (by creating a file backend.tf
in each Terraform
component's folder), or you can set up Atmos to generate the backend configuration for each component in the stacks automatically. While
auto-generating the backend config file is helpful and saves you from creating the backend files for each component, it becomes a requirement
when you provision multiple instances of a Terraform component into the same environment (same account and region).
You can provision more than one instance of the same Terraform component (with the same or different settings) into the same environment by defining many Atmos components that provide configuration for the Terraform component.
For more information on configuring and provision multiple instances of a Terraform component, refer to Multiple Component Instances Atmos Design Patterns
For example, the following config shows how to define two Atmos
components, vpc/1
and vpc/2
, which both point to the same Terraform component vpc
:
If we manually create a backend.tf
file for the vpc
Terraform component in the components/terraform/vpc
folder
using workspace_key_prefix: "vpc"
, then both vpc/1
and vpc/2
Atmos components will use the same workspace_key_prefix
, and they will
not function correctly.
On the other hand, if we configure Atmos to auto-generate the backend config file, then each component will have a different workspace_key_prefix
auto-generated by Atmos by using the Atmos component name (or you can override this behavior by specifying workspace_key_prefix
for each component
in the component manifest in the backend.s3.workspace_key_prefix
section).
For example, when the command atmos terraform apply vpc/1 -s plat-ue2-dev
is executed, the following backend.tf.json
file is generated in the
components/terraform/vpc
folder:
Similarly, when the command atmos terraform apply vpc/2 -s plat-ue2-dev
is executed, the following backend.tf.json
file is generated in the
components/terraform/vpc
folder:
The generated files will have different workspace_key_prefix
attribute auto-generated by Atmos.
For this reason, configuring Atmos to auto-generate the backend configuration for the components in the stacks is recommended for all supported backend types.