Introduction
Learn how to configure Terraform to use remote state storage in an Atlas S3 bucket. Remote state provides better collaboration, state locking, and persistence for your Terraform deployments.
Prerequisites
Before you begin, ensure you have:
- An Atlas Cloud Platform account
- Terraform installed on your local machine
- Atlas S3 bucket credentials (see below)
Step 1: Create an S3 Bucket for Remote State
If you haven’t already created an S3 bucket, follow our Website hosting on a static S3 bucket tutorial to create one. For remote state storage:
- Navigate to Atlas Storage in your Atlas Cloud Platform
- Click “Create Bucket”
- Use a descriptive name like
terraform-state, using lowercase letters, digits, and hyphens only (see Buckets for the naming rule) - Set the access policy to “Private” (recommended for state files)
- Click “OK”
Step 2: Get Your S3 Bucket Credentials
- Navigate to your bucket in Atlas Storage
- Click on the bucket name to view details
- Go to the “Details” tab
- Copy the Access Key and Secret Key
- Note your bucket name and region (if applicable)
Step 3: Configure Terraform Backend
Backend blocks cannot interpolate variables or reference var.*. Every value must be a static literal, or it is omitted from the block and supplied at init time through partial configuration (-backend-config) or environment variables. Keep credentials out of the backend block: export them as AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY before running terraform init.
Add the following backend configuration to your Terraform project. You can either:
Option A: Add to existing main.tf
Add this block at the top of your main.tf:
terraform {
backend "s3" {
bucket = "terraform-state"
key = "keycloak/terraform.tfstate"
region = "us-east-1" # Required by the SDK but ignored by Atlas object storage
endpoints = {
s3 = "https://s3.runatlas.is"
}
skip_credentials_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
skip_region_validation = true
}
required_providers {
cloudstack = {
source = "cloudstack/cloudstack"
version = "0.6.0"
}
}
}Option B: Create separate backend.tf
terraform {
backend "s3" {
bucket = "terraform-state"
key = "keycloak/terraform.tfstate"
region = "us-east-1" # Required by the SDK but ignored by Atlas object storage
endpoints = {
s3 = "https://s3.runatlas.is"
}
skip_credentials_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
skip_region_validation = true
}
}Step 4: Supply S3 Credentials and Set Variables
The S3 backend reads its credentials from the environment, not from Terraform variables. Export the bucket Access Key and Secret Key before running any Terraform command:
export AWS_ACCESS_KEY_ID="your-s3-access-key"
export AWS_SECRET_ACCESS_KEY="your-s3-secret-key"Keep these out of shell history and version control. For repeatable runs, store them in a tool such as direnv or your secrets manager rather than typing them on the command line.
Add the remaining infrastructure values to your terraform.tfvars. This file holds secrets, so add it to .gitignore and never commit it; alternatively, supply secrets via TF_VAR_* environment variables instead of a file:
# CloudStack Configuration
cloudstack_api_url = "https://sky.runatlas.is/client/api"
cloudstack_api_key = "your-cloudstack-api-key"
cloudstack_secret_key = "your-cloudstack-secret-key"
# Infrastructure Configuration
zone = "is1"
instance_service_offering = "Atlas.a5"
instance_template = "Ubuntu 24.04 LTS"
environment = "production"
# Keycloak Configuration
# Generate your own (e.g. `openssl rand -base64 24`); do not reuse these placeholders.
keycloak_admin_password = "<CHANGE_ME>"
keycloak_db_password = "<CHANGE_ME>"List the zones and offerings available to your account with cmk list zones and cmk list serviceofferings if you need values other than the defaults above.
Step 5: Initialize Terraform with Remote State
Make sure AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are exported (see Step 4), then initialize Terraform to configure the remote backend:
terraform initTerraform will prompt you to confirm the migration to remote state. Type yes to proceed.
Step 6: Verify Remote State Configuration
Check that your state is now stored remotely:
terraform state pullThis will display the current state file content from your S3 bucket.
Best Practices
Security Considerations
- Private Bucket: Keep your state bucket private
- Versioning: Enable bucket versioning to track state changes
Organization
- Key Structure: Use descriptive key paths like
project/environment/terraform.tfstate - Separate Buckets: Consider separate buckets for different environments (dev/staging/prod) instead of path-based environments
Example: Complete Configuration
Here’s a complete example for a Keycloak deployment:
backend.tf:
terraform {
backend "s3" {
bucket = "terraform-state"
key = "keycloak/production/terraform.tfstate"
region = "us-east-1" # Required by the SDK but ignored by Atlas object storage
endpoints = {
s3 = "https://s3.runatlas.is"
}
skip_credentials_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
skip_region_validation = true
}
}terraform.tfvars:
# Atlas CloudStack Configuration
cloudstack_api_url = "https://sky.runatlas.is/client/api"
cloudstack_api_key = "your-cloudstack-api-key"
cloudstack_secret_key = "your-cloudstack-secret-key"
# Infrastructure Configuration
zone = "is1"
instance_service_offering = "Atlas.a5"
instance_template = "Ubuntu 24.04 LTS"
environment = "production"
# Keycloak Configuration
# Generate your own (e.g. `openssl rand -base64 24`); do not reuse these placeholders.
keycloak_admin_password = "<CHANGE_ME>"
keycloak_db_password = "<CHANGE_ME>"Your Terraform state is now stored remotely in an Atlas S3 bucket, with state shared across collaborators and persisted between runs.