Deploy AWS CloudFormation Stack Instances

The aws:cloudformation/stackInstances:StackInstances resource, part of the Pulumi AWS provider, deploys CloudFormation stack instances from a StackSet to target accounts and regions. This guide focuses on three capabilities: account-specific targeting, organizational unit deployment, and cross-account IAM configuration.

Stack instances depend on an existing StackSet and require IAM execution roles in each target account. The examples are intentionally small. Combine them with your own StackSet definitions and IAM infrastructure.

Deploy stack instances to specific accounts and regions

Most StackSet deployments target specific AWS accounts and regions where you want identical infrastructure.

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}

The accounts property lists AWS account IDs where stack instances will be created. The regions property specifies which regions receive instances in each account. CloudFormation creates one stack instance per account-region pair, deploying the StackSet’s template to all combinations.

Configure execution roles in target accounts

Before StackSets can deploy to target accounts, those accounts need an execution role that trusts the administrator account.

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’s assumeRolePolicy grants the administrator account’s StackSet role permission to assume it via sts:AssumeRole. The role policy grants CloudFormation permissions to create resources defined in the StackSet template. This role must exist in each target account before stack instances deploy.

Deploy to organizational units automatically

Organizations with many accounts often deploy to entire OUs rather than maintaining explicit account lists.

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 the accounts list with organizationalUnitIds, targeting all accounts in those OUs. New accounts added to the OU automatically receive stack instances. StackSets never deploy to the organization management account, even if it’s in the targeted OU.

Beyond these examples

These snippets focus on specific stack instance features: account-specific and OU-based targeting, and cross-account IAM trust configuration. They’re intentionally minimal rather than full multi-account deployment solutions.

The examples reference pre-existing infrastructure such as CloudFormation StackSets, AWS Organizations structure for OU deployments, and IAM administration roles in the source account. They focus on deploying stack instances rather than creating the StackSet or organization structure.

To keep things focused, common stack instance patterns are omitted, including:

  • Parameter overrides per instance (parameterOverrides)
  • Operation preferences for deployment control (operationPreferences)
  • Stack retention on deletion (retainStacks)
  • Delegated administrator mode (callAs)

These omissions are intentional: the goal is to illustrate how stack instance deployment 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 FREE

Frequently Asked Questions

Deployment Targets & Scope
Should I use accounts or deploymentTargets to specify where stack instances are created?
Use accounts to target specific AWS account IDs, or deploymentTargets to target organizational units. You can’t use both in the same configuration.
Why aren't my stack instances deploying to the organization management account?
Stack sets don’t deploy instances to the organization management account, even if it’s included in an OU. To target the management account, use the accounts property with its account ID.
How do I deploy stack instances to specific AWS accounts?
Specify the target account IDs in the accounts property and list the desired regions. For example: accounts: ["123456789012", "234567890123"] and regions: ["us-east-1", "us-west-2"].
How do I deploy stack instances across my entire organization?
Use deploymentTargets with organizationalUnitIds pointing to your organization’s root or specific OUs, instead of listing individual accounts.
IAM & Permissions
What IAM permissions do target accounts need for stack instances?
Target accounts need an execution role named AWSCloudFormationStackSetExecutionRole with permissions for CloudFormation, S3, SNS, and any resources defined in your stack set template. The role must trust the administrator account’s role.
What's the difference between SELF and DELEGATED_ADMIN for callAs?
Use SELF (default) when acting as an account administrator in the organization’s management account, or DELEGATED_ADMIN when acting as a delegated administrator in a member account.
Drift Detection & State Management
Why isn't drift detection working for my stack instances?
Drift detection has limitations: it’s not possible for most of the deploymentTargets configuration, and for parameterOverrides it only works for the first account and region.
How do I retain stacks when destroying stack instances?
Set retainStacks = true and apply it successfully before running the destroy operation. You can’t reassociate retained stacks or add them to a new stack set later.
Configuration & Immutability
Can I change the stack set name after creating stack instances?
No, stackSetName is immutable and can’t be changed after creation. You’ll need to create new stack instances with a different stack set.

Using a different cloud?

Explore integration guides for other cloud providers: