The aws:cloudformation/stackInstances:StackInstances resource, part of the Pulumi AWS provider, manages the deployment of CloudFormation stacks to specific accounts and regions from a StackSet. This guide focuses on three capabilities: account-based targeting, organizational unit targeting, and IAM execution role setup.
Stack instances require an existing StackSet and IAM execution roles in all target accounts. The examples are intentionally small. Combine them with your own StackSet definitions and organizational structure.
Configure execution role in target accounts
Before deploying stack instances, each target account needs an IAM execution role that trusts the administrator account and has permissions to create the resources defined in your CloudFormation template.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy = aws.iam.getPolicyDocument({
statements: [{
actions: ["sts:AssumeRole"],
effect: "Allow",
principals: [{
identifiers: [aWSCloudFormationStackSetAdministrationRole.arn],
type: "AWS",
}],
}],
});
const aWSCloudFormationStackSetExecutionRole = new aws.iam.Role("AWSCloudFormationStackSetExecutionRole", {
assumeRolePolicy: aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy.then(aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy => aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy.json),
name: "AWSCloudFormationStackSetExecutionRole",
});
// Documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-prereqs.html
// Additional IAM permissions necessary depend on the resources defined in the StackSet template
const aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy = aws.iam.getPolicyDocument({
statements: [{
actions: [
"cloudformation:*",
"s3:*",
"sns:*",
],
effect: "Allow",
resources: ["*"],
}],
});
const aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicyRolePolicy = new aws.iam.RolePolicy("AWSCloudFormationStackSetExecutionRole_MinimumExecutionPolicy", {
name: "MinimumExecutionPolicy",
policy: aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy.then(aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy => aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy.json),
role: aWSCloudFormationStackSetExecutionRole.name,
});
import pulumi
import pulumi_aws as aws
a_ws_cloud_formation_stack_set_execution_role_assume_role_policy = aws.iam.get_policy_document(statements=[{
"actions": ["sts:AssumeRole"],
"effect": "Allow",
"principals": [{
"identifiers": [a_ws_cloud_formation_stack_set_administration_role["arn"]],
"type": "AWS",
}],
}])
a_ws_cloud_formation_stack_set_execution_role = aws.iam.Role("AWSCloudFormationStackSetExecutionRole",
assume_role_policy=a_ws_cloud_formation_stack_set_execution_role_assume_role_policy.json,
name="AWSCloudFormationStackSetExecutionRole")
# Documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-prereqs.html
# Additional IAM permissions necessary depend on the resources defined in the StackSet template
a_ws_cloud_formation_stack_set_execution_role_minimum_execution_policy = aws.iam.get_policy_document(statements=[{
"actions": [
"cloudformation:*",
"s3:*",
"sns:*",
],
"effect": "Allow",
"resources": ["*"],
}])
a_ws_cloud_formation_stack_set_execution_role_minimum_execution_policy_role_policy = aws.iam.RolePolicy("AWSCloudFormationStackSetExecutionRole_MinimumExecutionPolicy",
name="MinimumExecutionPolicy",
policy=a_ws_cloud_formation_stack_set_execution_role_minimum_execution_policy.json,
role=a_ws_cloud_formation_stack_set_execution_role.name)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
{
Actions: []string{
"sts:AssumeRole",
},
Effect: pulumi.StringRef("Allow"),
Principals: []iam.GetPolicyDocumentStatementPrincipal{
{
Identifiers: interface{}{
aWSCloudFormationStackSetAdministrationRole.Arn,
},
Type: "AWS",
},
},
},
},
}, nil);
if err != nil {
return err
}
aWSCloudFormationStackSetExecutionRole, err := iam.NewRole(ctx, "AWSCloudFormationStackSetExecutionRole", &iam.RoleArgs{
AssumeRolePolicy: pulumi.String(aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy.Json),
Name: pulumi.String("AWSCloudFormationStackSetExecutionRole"),
})
if err != nil {
return err
}
// Documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-prereqs.html
// Additional IAM permissions necessary depend on the resources defined in the StackSet template
aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
{
Actions: []string{
"cloudformation:*",
"s3:*",
"sns:*",
},
Effect: pulumi.StringRef("Allow"),
Resources: []string{
"*",
},
},
},
}, nil);
if err != nil {
return err
}
_, err = iam.NewRolePolicy(ctx, "AWSCloudFormationStackSetExecutionRole_MinimumExecutionPolicy", &iam.RolePolicyArgs{
Name: pulumi.String("MinimumExecutionPolicy"),
Policy: pulumi.String(aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy.Json),
Role: aWSCloudFormationStackSetExecutionRole.Name,
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy = Aws.Iam.GetPolicyDocument.Invoke(new()
{
Statements = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
{
Actions = new[]
{
"sts:AssumeRole",
},
Effect = "Allow",
Principals = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
{
Identifiers = new[]
{
aWSCloudFormationStackSetAdministrationRole.Arn,
},
Type = "AWS",
},
},
},
},
});
var aWSCloudFormationStackSetExecutionRole = new Aws.Iam.Role("AWSCloudFormationStackSetExecutionRole", new()
{
AssumeRolePolicy = aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
Name = "AWSCloudFormationStackSetExecutionRole",
});
// Documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-prereqs.html
// Additional IAM permissions necessary depend on the resources defined in the StackSet template
var aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy = Aws.Iam.GetPolicyDocument.Invoke(new()
{
Statements = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
{
Actions = new[]
{
"cloudformation:*",
"s3:*",
"sns:*",
},
Effect = "Allow",
Resources = new[]
{
"*",
},
},
},
});
var aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicyRolePolicy = new Aws.Iam.RolePolicy("AWSCloudFormationStackSetExecutionRole_MinimumExecutionPolicy", new()
{
Name = "MinimumExecutionPolicy",
Policy = aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
Role = aWSCloudFormationStackSetExecutionRole.Name,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.iam.RolePolicy;
import com.pulumi.aws.iam.RolePolicyArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
final var aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
.statements(GetPolicyDocumentStatementArgs.builder()
.actions("sts:AssumeRole")
.effect("Allow")
.principals(GetPolicyDocumentStatementPrincipalArgs.builder()
.identifiers(aWSCloudFormationStackSetAdministrationRole.arn())
.type("AWS")
.build())
.build())
.build());
var aWSCloudFormationStackSetExecutionRole = new Role("aWSCloudFormationStackSetExecutionRole", RoleArgs.builder()
.assumeRolePolicy(aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy.json())
.name("AWSCloudFormationStackSetExecutionRole")
.build());
// Documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-prereqs.html
// Additional IAM permissions necessary depend on the resources defined in the StackSet template
final var aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
.statements(GetPolicyDocumentStatementArgs.builder()
.actions(
"cloudformation:*",
"s3:*",
"sns:*")
.effect("Allow")
.resources("*")
.build())
.build());
var aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicyRolePolicy = new RolePolicy("aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicyRolePolicy", RolePolicyArgs.builder()
.name("MinimumExecutionPolicy")
.policy(aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy.json())
.role(aWSCloudFormationStackSetExecutionRole.name())
.build());
}
}
resources:
aWSCloudFormationStackSetExecutionRole:
type: aws:iam:Role
name: AWSCloudFormationStackSetExecutionRole
properties:
assumeRolePolicy: ${aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy.json}
name: AWSCloudFormationStackSetExecutionRole
aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicyRolePolicy:
type: aws:iam:RolePolicy
name: AWSCloudFormationStackSetExecutionRole_MinimumExecutionPolicy
properties:
name: MinimumExecutionPolicy
policy: ${aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy.json}
role: ${aWSCloudFormationStackSetExecutionRole.name}
variables:
aWSCloudFormationStackSetExecutionRoleAssumeRolePolicy:
fn::invoke:
function: aws:iam:getPolicyDocument
arguments:
statements:
- actions:
- sts:AssumeRole
effect: Allow
principals:
- identifiers:
- ${aWSCloudFormationStackSetAdministrationRole.arn}
type: AWS
# Documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-prereqs.html
# Additional IAM permissions necessary depend on the resources defined in the StackSet template
aWSCloudFormationStackSetExecutionRoleMinimumExecutionPolicy:
fn::invoke:
function: aws:iam:getPolicyDocument
arguments:
statements:
- actions:
- cloudformation:*
- s3:*
- sns:*
effect: Allow
resources:
- '*'
The execution role must trust the administrator role via its assumeRolePolicy, allowing CloudFormation to assume it during deployment. The role’s permissions must cover all resources your template creates. The example shows minimum CloudFormation permissions; you’ll need to add permissions for any AWS services your template uses (EC2, RDS, Lambda, etc.).
Deploy stack instances to specific accounts and regions
Most StackSet deployments target a known list of AWS accounts across multiple regions, creating identical infrastructure in each location.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.cloudformation.StackInstances("example", {
accounts: [
"123456789012",
"234567890123",
],
regions: [
"us-east-1",
"us-west-2",
],
stackSetName: exampleAwsCloudformationStackSet.name,
});
import pulumi
import pulumi_aws as aws
example = aws.cloudformation.StackInstances("example",
accounts=[
"123456789012",
"234567890123",
],
regions=[
"us-east-1",
"us-west-2",
],
stack_set_name=example_aws_cloudformation_stack_set["name"])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudformation"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := cloudformation.NewStackInstances(ctx, "example", &cloudformation.StackInstancesArgs{
Accounts: pulumi.StringArray{
pulumi.String("123456789012"),
pulumi.String("234567890123"),
},
Regions: pulumi.StringArray{
pulumi.String("us-east-1"),
pulumi.String("us-west-2"),
},
StackSetName: pulumi.Any(exampleAwsCloudformationStackSet.Name),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.CloudFormation.StackInstances("example", new()
{
Accounts = new[]
{
"123456789012",
"234567890123",
},
Regions = new[]
{
"us-east-1",
"us-west-2",
},
StackSetName = exampleAwsCloudformationStackSet.Name,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudformation.StackInstances;
import com.pulumi.aws.cloudformation.StackInstancesArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new StackInstances("example", StackInstancesArgs.builder()
.accounts(
"123456789012",
"234567890123")
.regions(
"us-east-1",
"us-west-2")
.stackSetName(exampleAwsCloudformationStackSet.name())
.build());
}
}
resources:
example:
type: aws:cloudformation:StackInstances
properties:
accounts:
- '123456789012'
- '234567890123'
regions:
- us-east-1
- us-west-2
stackSetName: ${exampleAwsCloudformationStackSet.name}
CloudFormation creates one stack instance per account-region pair. The accounts property lists target AWS account IDs; regions specifies where to deploy in each account. The stackSetName references your existing StackSet definition. All target accounts must have the execution role configured before deployment succeeds.
Deploy to organizational units automatically
Organizations with many accounts often deploy to entire organizational units rather than maintaining explicit account lists, allowing new accounts to inherit stacks automatically.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.cloudformation.StackInstances("example", {
deploymentTargets: {
organizationalUnitIds: [exampleAwsOrganizationsOrganization.roots[0].id],
},
regions: [
"us-west-2",
"us-east-1",
],
stackSetName: exampleAwsCloudformationStackSet.name,
});
import pulumi
import pulumi_aws as aws
example = aws.cloudformation.StackInstances("example",
deployment_targets={
"organizational_unit_ids": [example_aws_organizations_organization["roots"][0]["id"]],
},
regions=[
"us-west-2",
"us-east-1",
],
stack_set_name=example_aws_cloudformation_stack_set["name"])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudformation"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := cloudformation.NewStackInstances(ctx, "example", &cloudformation.StackInstancesArgs{
DeploymentTargets: &cloudformation.StackInstancesDeploymentTargetsArgs{
OrganizationalUnitIds: pulumi.StringArray{
exampleAwsOrganizationsOrganization.Roots[0].Id,
},
},
Regions: pulumi.StringArray{
pulumi.String("us-west-2"),
pulumi.String("us-east-1"),
},
StackSetName: pulumi.Any(exampleAwsCloudformationStackSet.Name),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.CloudFormation.StackInstances("example", new()
{
DeploymentTargets = new Aws.CloudFormation.Inputs.StackInstancesDeploymentTargetsArgs
{
OrganizationalUnitIds = new[]
{
exampleAwsOrganizationsOrganization.Roots[0].Id,
},
},
Regions = new[]
{
"us-west-2",
"us-east-1",
},
StackSetName = exampleAwsCloudformationStackSet.Name,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudformation.StackInstances;
import com.pulumi.aws.cloudformation.StackInstancesArgs;
import com.pulumi.aws.cloudformation.inputs.StackInstancesDeploymentTargetsArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new StackInstances("example", StackInstancesArgs.builder()
.deploymentTargets(StackInstancesDeploymentTargetsArgs.builder()
.organizationalUnitIds(exampleAwsOrganizationsOrganization.roots()[0].id())
.build())
.regions(
"us-west-2",
"us-east-1")
.stackSetName(exampleAwsCloudformationStackSet.name())
.build());
}
}
resources:
example:
type: aws:cloudformation:StackInstances
properties:
deploymentTargets:
organizationalUnitIds:
- ${exampleAwsOrganizationsOrganization.roots[0].id}
regions:
- us-west-2
- us-east-1
stackSetName: ${exampleAwsCloudformationStackSet.name}
The deploymentTargets property replaces explicit account lists with organizational unit IDs. CloudFormation automatically discovers all accounts in the specified OUs and deploys to them. New accounts added to the OU later will receive stack instances automatically. This approach scales better than account-based targeting for large organizations.
Beyond these examples
These snippets focus on specific stack instance features: account-based and OU-based targeting, and IAM execution role configuration. They’re intentionally minimal rather than full multi-account deployment solutions.
The examples require pre-existing infrastructure such as CloudFormation StackSet, AWS Organizations structure (for OU targeting), and IAM administrator role in management account. They focus on configuring stack instances rather than provisioning the StackSet or organizational structure.
To keep things focused, common stack instance patterns are omitted, including:
- Parameter overrides per instance (parameterOverrides)
- Operation preferences (failure tolerance, parallelism)
- Stack retention on resource deletion (retainStacks)
- Delegated administrator configuration (callAs)
These omissions are intentional: the goal is to illustrate how stack instance targeting is wired, not provide drop-in multi-account modules. See the CloudFormation StackInstances resource reference for all available configuration options.
Let's deploy AWS CloudFormation Stack Instances
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Drift Detection
parameterOverrides is limited to the first account and region only, since each instance can have unique parameters.deploymentTargets argument.IAM & Permissions
Each target account must have an IAM role matching the executionRoleName from your stack set. This role needs:
- A trust relationship with the administrative account or admin IAM role
- Permissions to manage resources defined in your template
- CloudFormation stack operation permissions
See the IAM setup example for a complete configuration.
Deployment Targets
accounts to specify individual AWS account IDs, or use deploymentTargets to target AWS Organizations organizational units. You can specify either accounts or deploymentTargets, but not both.deploymentTargets.Lifecycle & Retention
retainStacks to true and apply it successfully before running the destroy operation. This must be done in a separate apply, not in the same operation that destroys the resource.stackSetName is immutable and cannot be changed after the resource is created.Using a different cloud?
Explore integration guides for other cloud providers: