Multi-Account Terraform
Simfra supports multiple AWS accounts on a single instance. Each account has its own credentials, resources, and IAM policies. This lets you test cross-account patterns like shared S3 buckets, cross-account role assumption, and resource policies.
Prerequisites
- Simfra running on
localhost:4599 - Environment variables set for the default account (see Provider Configuration)
Creating Accounts
Use the Simfra admin API to create accounts:
# Create account 111111111111
curl -s -X POST http://localhost:4599/_simfra/accounts \
-d '{"accountId":"111111111111"}' | jq .
Response:
{
"accountId": "111111111111",
"rootAccessKeyId": "AKIA111111111EXAMPLE",
"rootSecretAccessKey": "wJalr111111111SECRET111111111KEY",
"createdAt": "2026-04-22T12:00:00Z"
}
The response contains the root credentials for the new account. Save these for use in Terraform provider blocks.
Retrieving Credentials Later
curl -s http://localhost:4599/_simfra/accounts/111111111111 | jq .
Creating Accounts with Bootstrap
You can bootstrap a new account at creation time:
curl -s -X POST http://localhost:4599/_simfra/accounts \
-d '{"accountId":"111111111111","bootstrap":"standard","region":"us-east-1"}' | jq .
This creates the account and runs standard bootstrap (default VPC, KMS keys) in one call.
Listing and Deleting Accounts
# List all accounts
curl -s http://localhost:4599/_simfra/accounts | jq .
# Delete an account and all its resources
curl -s -X DELETE http://localhost:4599/_simfra/accounts/111111111111
Terraform Provider Aliases
Use provider aliases with different credentials to operate on separate accounts:
# Default account (000000000000)
provider "aws" {
region = "us-east-1"
}
# Second account (111111111111)
provider "aws" {
alias = "account_b"
region = "us-east-1"
access_key = "AKIA111111111EXAMPLE"
secret_key = "wJalr111111111SECRET111111111KEY"
}
AWS_ENDPOINT_URL applies to all provider instances, so both aliases route to the same Simfra instance. The SigV4 signature determines which account each request targets.
Example: Cross-Account S3 Bucket Policy
Account A creates a bucket and grants read access to Account B:
# Account A: create bucket with cross-account policy
resource "aws_s3_bucket" "shared" {
bucket = "shared-data-bucket"
}
resource "aws_s3_bucket_policy" "cross_account" {
bucket = aws_s3_bucket.shared.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "AllowAccountBRead"
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::111111111111:root" }
Action = ["s3:GetObject", "s3:ListBucket"]
Resource = [
aws_s3_bucket.shared.arn,
"${aws_s3_bucket.shared.arn}/*"
]
}]
})
}
# Account A: upload a test object
resource "aws_s3_object" "test" {
bucket = aws_s3_bucket.shared.id
key = "data/test.txt"
content = "cross-account data"
}
# Account B: read the object (uses the bucket policy for authorization)
data "aws_s3_object" "read_cross_account" {
provider = aws.account_b
bucket = aws_s3_bucket.shared.id
key = "data/test.txt"
depends_on = [
aws_s3_bucket_policy.cross_account,
aws_s3_object.test,
]
}
output "cross_account_content" {
value = data.aws_s3_object.read_cross_account.body
}
Simfra evaluates the bucket policy on Account B's request, just like AWS does.
Example: Cross-Account Role Assumption
Account A creates a role that Account B can assume:
# Account A: create a role with a trust policy for Account B
resource "aws_iam_role" "cross_account" {
name = "cross-account-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::111111111111:root" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy_attachment" "cross_account" {
role = aws_iam_role.cross_account.name
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
output "cross_account_role_arn" {
value = aws_iam_role.cross_account.arn
}
Account B assumes the role using the AWS CLI or SDK:
# Using Account B's credentials, assume the role in Account A
AWS_ACCESS_KEY_ID=AKIA111111111EXAMPLE \
AWS_SECRET_ACCESS_KEY=wJalr111111111SECRET111111111KEY \
aws sts assume-role \
--role-arn arn:aws:iam::000000000000:role/cross-account-role \
--role-session-name test-session
The response includes temporary credentials scoped to Account A with the role's permissions.
Example: Cross-Account KMS Key Access
Account A creates a KMS key with a key policy that grants Account B usage:
resource "aws_kms_key" "shared" {
description = "Shared encryption key"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowAccountAAdmin"
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::000000000000:root" }
Action = "kms:*"
Resource = "*"
},
{
Sid = "AllowAccountBEncrypt"
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::111111111111:root" }
Action = ["kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey"]
Resource = "*"
}
]
})
}
Setup Script
A complete script that creates two accounts and sets up Terraform:
#!/bin/bash
set -euo pipefail
SIMFRA=http://localhost:4599
# Create Account B
ACCOUNT_B=$(curl -s -X POST "$SIMFRA/_simfra/accounts" \
-d '{"accountId":"111111111111","bootstrap":"standard"}')
ACCOUNT_B_KEY=$(echo "$ACCOUNT_B" | jq -r .rootAccessKeyId)
ACCOUNT_B_SECRET=$(echo "$ACCOUNT_B" | jq -r .rootSecretAccessKey)
# Write a Terraform vars file with Account B credentials
cat > account_b.auto.tfvars <<EOF
account_b_access_key = "$ACCOUNT_B_KEY"
account_b_secret_key = "$ACCOUNT_B_SECRET"
EOF
# Set default account credentials
export AWS_ENDPOINT_URL=$SIMFRA
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_DEFAULT_REGION=us-east-1
terraform init
terraform apply
With the corresponding Terraform variables:
variable "account_b_access_key" {
type = string
sensitive = true
}
variable "account_b_secret_key" {
type = string
sensitive = true
}
provider "aws" {
alias = "account_b"
region = "us-east-1"
access_key = var.account_b_access_key
secret_key = var.account_b_secret_key
}
Next Steps
- Provider Configuration - the canonical single-account Terraform setup
- Bootstrapping Your Account - bootstrap each account with default VPCs and KMS keys
- Testing Patterns - patterns for testing Terraform modules