Deploying consistent infrastructure and security controls across multiple AWS accounts and regions is a critical challenge for organizations managing large-scale AWS environments. This module solves the operational complexity of distributing CloudFormation templates organization-wide by providing a Terraform-native interface to AWS CloudFormation StackSets.
The primary use case is enabling centralized infrastructure provisioning in AWS Organizations environments—whether rolling out IAM permissions boundaries, deploying security baselines, distributing networking configurations, or ensuring compliance controls are consistently applied across development, staging, and production accounts.
The module orchestrates three key AWS StackSet capabilities:
- StackSet Definition: Creates a CloudFormation StackSet with your inline template or template URL, parameters, and operational preferences
- Organizational Unit Deployment: Automatically deploys stack instances to all accounts within specified OUs, with optional account exclusions
- Account-Based Deployment: Explicitly targets specific account IDs across one or more regions
When using the SERVICE_MANAGED permission model (recommended for AWS Organizations), the module enables auto-deployment, ensuring new accounts automatically receive the stack as they join targeted OUs. Stack instances are deployed with configurable concurrency controls and failure tolerance thresholds to balance speed and safety during multi-account rollouts.
This module is designed for AWS Organizations environments and supports both:
- SERVICE_MANAGED mode: Leverages AWS Organizations integration for automatic stack distribution to OUs without manual IAM role management (recommended)
- SELF_MANAGED mode: Requires pre-configured IAM roles (
AWSCloudFormationStackSetAdministrationRoleandAWSCloudFormationStackSetExecutionRole) for cross-account deployments
The module is commonly used in AWS Landing Zone architectures, AWS Control Tower environments, and multi-account governance strategies where centralized infrastructure distribution is required.
- Service-Managed IAM Roles: Defaults to
SERVICE_MANAGEDpermission model, eliminating the need to manually provision cross-account IAM roles and reducing misconfiguration risk - CloudFormation Capabilities: Pre-configured with
CAPABILITY_IAM,CAPABILITY_NAMED_IAM, andCAPABILITY_AUTO_EXPANDto support IAM resource provisioning and nested stacks - Stack Retention Control: Configurable retention behavior when accounts are removed from organizational units, preventing accidental resource deletion (default: retain)
- Multi-Region Deployment: Deploy to a single region or broadcast across multiple regions simultaneously with parallel execution
- Hybrid Targeting: Supports both OU-based deployment (for organizational governance) and explicit account-based targeting (for exceptions or testing)
- Account Exclusions: Fine-grained control to exclude specific accounts from OU-level deployments without modifying organizational structure
- Template Parameterization: Pass CloudFormation parameters dynamically, enabling per-environment customization of the same template
- Template Source Flexibility: Provide templates inline (
template) or via URL (template_url) for larger CloudFormation documents
- Concurrency Management: Configurable
max_concurrent_count(default: 10 concurrent deployments) to control rollout velocity and API throttling - Failure Tolerance: Define acceptable failure thresholds before halting organization-wide operations, preventing cascading failures
- Auto-Deployment: Automatically provisions stacks to newly created accounts in targeted OUs without manual intervention
- Terraform State Integration: Full Terraform lifecycle management with proper dependency handling and minimal drift risk
- Centralized Policy Enforcement: Enable organization-wide compliance by distributing security controls, tagging policies, or permissions boundaries via CloudFormation
- Audit-Ready Deployment: CloudFormation stacksets provide built-in change tracking and drift detection capabilities
- Consistent Configuration: Ensures identical resource configurations across accounts, reducing compliance audit surface area
This is the most common deployment pattern—distributing a CloudFormation template to all accounts in specific organizational units across multiple regions:
module "security_baseline" {
source = "appvia/stackset/aws"
name = "security-baseline"
description = "Deploys mandatory security controls across production accounts"
template = file("${path.module}/templates/security-baseline.yml")
# Deploy to production and infrastructure OUs in two regions
organizational_units = [
"ou-xxxx-production",
"ou-xxxx-infrastructure"
]
enabled_regions = ["us-east-1", "eu-west-1"]
# CloudFormation parameters
parameters = {
SecurityContactEmail = "security@example.com"
EnableGuardDuty = "true"
}
# Standard concurrency settings
max_concurrent_count = 10
failure_tolerance_count = 2
tags = {
ManagedBy = "terraform"
Purpose = "security-compliance"
Environment = "production"
}
}This configuration will:
- Deploy the CloudFormation template to all accounts in the specified OUs
- Automatically provision to new accounts as they join these OUs
- Roll out to both us-east-1 and eu-west-1 regions in parallel
- Tolerate up to 2 failures before halting the operation
For templates that exceed CloudFormation inline body limits, use template_url instead of template:
module "security_baseline_large_template" {
source = "appvia/stackset/aws"
name = "security-baseline-large-template"
description = "Deploy security controls using a template stored in S3"
template_url = "https://s3.amazonaws.com/example-bucket/security-baseline.yml"
organizational_units = ["ou-xxxx-production"]
enabled_regions = ["us-east-1"]
parameters = {}
tags = {}
}Exactly one of template or template_url must be set.
Advanced scenarios requiring account exclusions, single-region deployment, and integration with data sources:
# Fetch current region dynamically
data "aws_region" "current" {}
# Read organizational units from a data source
data "aws_organizations_organizational_units" "workloads" {
parent_id = "r-xxxx"
}
module "permissions_boundary" {
source = "appvia/stackset/aws"
name = "pipeline-permissions-boundary"
description = "IAM permissions boundary for CI/CD pipelines"
template = templatefile("${path.module}/templates/boundary.yml.tpl", {
allowed_services = ["s3", "dynamodb", "lambda", "cloudwatch"]
denied_actions = ["iam:*", "organizations:*"]
})
# Deploy to all workload OUs except sandbox accounts
organizational_units = [
for ou in data.aws_organizations_organizational_units.workloads.children : ou.id
]
# Explicitly exclude test accounts that need unrestricted permissions
exclude_accounts = [
"123456789012", # test-account-1
"234567890123", # test-account-2
]
# Deploy only to current region (avoid duplicating regional resources)
region = data.aws_region.current.name
# Higher concurrency for faster rollout across many accounts
max_concurrent_count = 25
failure_tolerance_count = 5
# CloudFormation parameters with conditional logic
parameters = {
BoundaryName = "PipelineBoundary"
MaxSessionDuration = "3600"
Environment = terraform.workspace
}
tags = merge(
var.common_tags,
{
StackSetType = "permissions-boundary"
Compliance = "required"
}
)
}This advanced example demonstrates:
- Dynamic OU discovery using data sources
- Templating CloudFormation with Terraform's
templatefilefunction - Excluding specific accounts from OU-wide deployments
- Single-region deployment to avoid regional resource conflicts
- Increased concurrency for large-scale rollouts
- Tag merging for inheritance patterns
When migrating existing manually-created StackSets to Terraform management or deploying to specific accounts instead of OUs:
# Import existing StackSet:
# terraform import module.legacy_stackset.aws_cloudformation_stack_set.stackset LegacyStackSetName
module "legacy_stackset" {
source = "appvia/stackset/aws"
name = "legacy-stackset" # Must match existing StackSet name for import
description = "Migrated from manual CloudFormation StackSet"
template = file("${path.module}/templates/legacy-template.yml")
# Use account-based deployment instead of OU targeting
# Useful for controlled migration or when OU structure is unavailable
accounts = [
"111111111111",
"222222222222",
"333333333333",
]
enabled_regions = ["us-east-1"]
# SELF_MANAGED mode if using existing IAM roles
permission_model = "SELF_MANAGED"
# Conservative settings for migration safety
max_concurrent_count = 3
failure_tolerance_count = 0 # Stop immediately on any failure
# Preserve stacks if accounts are later removed from the list
retain_stacks_on_account_removal = true
parameters = {}
tags = {
ManagedBy = "terraform"
MigratedFrom = "manual-cloudformation"
MigrationDate = "2026-02-12"
}
lifecycle {
# Prevent accidental changes to critical stack name
prevent_destroy = true
}
}
# Account-based deployment for selective rollout to specific environments
module "testing_stackset" {
source = "appvia/stackset/aws"
name = "feature-test-stack"
description = "Testing new feature in specific accounts before org-wide rollout"
template = file("${path.module}/templates/new-feature.yml")
# Explicitly target test accounts only
accounts = [
"444444444444", # dev-test-account
"555555555555", # staging-test-account
]
enabled_regions = ["us-west-2", "eu-central-1"]
parameters = {
FeatureFlag = "enabled"
LogLevel = "debug"
}
tags = {
Environment = "test"
Purpose = "feature-validation"
}
}This migration example shows:
- Importing existing StackSets into Terraform management
- Using
SELF_MANAGEDmode for environments without AWS Organizations integration - Account-based targeting for gradual rollouts or testing
- Conservative concurrency and failure settings for risk mitigation
- Lifecycle rules to prevent accidental destruction
- Deployment Time: StackSet operations can take 15-30 minutes for large-scale OU deployments (50+ accounts). CloudFormation executes stack creation sequentially within each account, regardless of concurrency settings
- OU Changes Not Tracked: If accounts are moved between OUs outside of Terraform, the module does not automatically detect and update stack instances. You must manually update the
organizational_unitsvariable - Regional Dependencies: CloudFormation StackSets deploy identically to all specified regions. If your template requires region-specific parameters or resources, consider using separate StackSets per region
- Service-Managed Requirement: The
SERVICE_MANAGEDpermission model requires AWS Control Tower or Organizations to be enabled with trusted access. New AWS accounts may take several hours to fully support StackSet deployments - Template Size Limit: CloudFormation templates are limited to 51,200 bytes when passed inline (
template). Usetemplate_urlfor larger templates hosted in S3 or other supported locations - Stack Instance Limits: AWS enforces a soft limit of 2,000 stack instances per StackSet. For organizations exceeding this, consider partitioning deployments across multiple StackSets
v2.0.0 (Planned):
- Minimum AWS provider version will increase to 6.0.0
v1.0.0:
- Initial stable release
- Requires AWS provider >= 5.0.0
- Defaults to
SERVICE_MANAGEDpermission model
| Name | Version |
|---|---|
| aws | >= 6.0.0 |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| description | The description of the cloudformation stack | string |
n/a | yes |
| name | The name of the cloudformation stack | string |
n/a | yes |
| tags | The tags to apply to the cloudformation stack | map(string) |
n/a | yes |
| accounts | When using an account deployments, the following accounts will be included | list(string) |
[] |
no |
| call_as | Specifies whether you are acting as an account administrator in the organization's management account or as a delegated administrator in a member account | string |
"SELF" |
no |
| capabilities | The capabilities required to deploy the cloudformation template | list(string) |
[ |
no |
| enabled_regions | The regions to deploy the cloudformation stack to (if empty, deploys to current region) | list(string) |
null |
no |
| exclude_accounts | When using an organizational deployments, the following accounts will be excluded | list(string) |
[] |
no |
| failure_tolerance_count | The number of failures that are tolerated before the stack operation is stopped | number |
0 |
no |
| max_concurrent_count | The maximum number of concurrent deployments | number |
10 |
no |
| organizational_units | The organizational units to deploy the stackset to | list(string) |
[] |
no |
| parameters | The parameters to pass to the cloudformation template | map(string) |
{} |
no |
| permission_model | Describes how the IAM roles required for your StackSet are created | string |
"SERVICE_MANAGED" |
no |
| region | The region to deploy the cloudformation template | string |
null |
no |
| retain_stacks_on_account_removal | Whether to retain stacks on account removal | bool |
true |
no |
| template | The body of the cloudformation template to deploy | string |
null |
no |
| template_url | The URL of the cloudformation template to deploy | string |
null |
no |
No outputs.
