Create AWS RDS Aurora Clusters

The aws:rds/cluster:Cluster resource, part of the Pulumi AWS provider, defines RDS Aurora clusters or Multi-AZ DB clusters: their engine selection, availability zones, backup configuration, and scaling behavior. This guide focuses on four capabilities: Aurora MySQL and PostgreSQL cluster creation, Multi-AZ cluster provisioning with storage configuration, Serverless v2 auto-scaling, and Secrets Manager password management.

Clusters typically reference VPC subnets, security groups, and KMS keys, though most examples use default VPC settings. Cluster instances must be added separately using aws.rds.ClusterInstance for non-serverless deployments. The examples are intentionally small. Combine them with your own networking, encryption, and monitoring configuration.

Create an Aurora MySQL cluster with backups

Most Aurora deployments start with a basic cluster configuration that specifies the engine, availability zones, and backup settings.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const _default = new aws.rds.Cluster("default", {
    clusterIdentifier: "aurora-cluster-demo",
    engine: aws.rds.EngineType.AuroraMysql,
    engineVersion: "5.7.mysql_aurora.2.03.2",
    availabilityZones: [
        "us-west-2a",
        "us-west-2b",
        "us-west-2c",
    ],
    databaseName: "mydb",
    masterUsername: "foo",
    masterPassword: "must_be_eight_characters",
    backupRetentionPeriod: 5,
    preferredBackupWindow: "07:00-09:00",
});
import pulumi
import pulumi_aws as aws

default = aws.rds.Cluster("default",
    cluster_identifier="aurora-cluster-demo",
    engine=aws.rds.EngineType.AURORA_MYSQL,
    engine_version="5.7.mysql_aurora.2.03.2",
    availability_zones=[
        "us-west-2a",
        "us-west-2b",
        "us-west-2c",
    ],
    database_name="mydb",
    master_username="foo",
    master_password="must_be_eight_characters",
    backup_retention_period=5,
    preferred_backup_window="07:00-09:00")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/rds"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := rds.NewCluster(ctx, "default", &rds.ClusterArgs{
			ClusterIdentifier: pulumi.String("aurora-cluster-demo"),
			Engine:            pulumi.String(rds.EngineTypeAuroraMysql),
			EngineVersion:     pulumi.String("5.7.mysql_aurora.2.03.2"),
			AvailabilityZones: pulumi.StringArray{
				pulumi.String("us-west-2a"),
				pulumi.String("us-west-2b"),
				pulumi.String("us-west-2c"),
			},
			DatabaseName:          pulumi.String("mydb"),
			MasterUsername:        pulumi.String("foo"),
			MasterPassword:        pulumi.String("must_be_eight_characters"),
			BackupRetentionPeriod: pulumi.Int(5),
			PreferredBackupWindow: pulumi.String("07:00-09:00"),
		})
		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 @default = new Aws.Rds.Cluster("default", new()
    {
        ClusterIdentifier = "aurora-cluster-demo",
        Engine = Aws.Rds.EngineType.AuroraMysql,
        EngineVersion = "5.7.mysql_aurora.2.03.2",
        AvailabilityZones = new[]
        {
            "us-west-2a",
            "us-west-2b",
            "us-west-2c",
        },
        DatabaseName = "mydb",
        MasterUsername = "foo",
        MasterPassword = "must_be_eight_characters",
        BackupRetentionPeriod = 5,
        PreferredBackupWindow = "07:00-09:00",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.rds.Cluster;
import com.pulumi.aws.rds.ClusterArgs;
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 default_ = new Cluster("default", ClusterArgs.builder()
            .clusterIdentifier("aurora-cluster-demo")
            .engine("aurora-mysql")
            .engineVersion("5.7.mysql_aurora.2.03.2")
            .availabilityZones(            
                "us-west-2a",
                "us-west-2b",
                "us-west-2c")
            .databaseName("mydb")
            .masterUsername("foo")
            .masterPassword("must_be_eight_characters")
            .backupRetentionPeriod(5)
            .preferredBackupWindow("07:00-09:00")
            .build());

    }
}
resources:
  default:
    type: aws:rds:Cluster
    properties:
      clusterIdentifier: aurora-cluster-demo
      engine: aurora-mysql
      engineVersion: 5.7.mysql_aurora.2.03.2
      availabilityZones:
        - us-west-2a
        - us-west-2b
        - us-west-2c
      databaseName: mydb
      masterUsername: foo
      masterPassword: must_be_eight_characters
      backupRetentionPeriod: 5
      preferredBackupWindow: 07:00-09:00

The engine and engineVersion properties select Aurora MySQL 2.x (MySQL 5.7 compatible). The availabilityZones array distributes cluster storage across three zones for high availability. The backupRetentionPeriod and preferredBackupWindow properties configure automated backups to run daily during a maintenance window, retaining snapshots for 5 days.

Create an Aurora PostgreSQL cluster

Teams running PostgreSQL workloads use Aurora PostgreSQL for managed replication and automatic failover while maintaining PostgreSQL compatibility.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const postgresql = new aws.rds.Cluster("postgresql", {
    clusterIdentifier: "aurora-cluster-demo",
    engine: aws.rds.EngineType.AuroraPostgresql,
    availabilityZones: [
        "us-west-2a",
        "us-west-2b",
        "us-west-2c",
    ],
    databaseName: "mydb",
    masterUsername: "foo",
    masterPassword: "must_be_eight_characters",
    backupRetentionPeriod: 5,
    preferredBackupWindow: "07:00-09:00",
});
import pulumi
import pulumi_aws as aws

postgresql = aws.rds.Cluster("postgresql",
    cluster_identifier="aurora-cluster-demo",
    engine=aws.rds.EngineType.AURORA_POSTGRESQL,
    availability_zones=[
        "us-west-2a",
        "us-west-2b",
        "us-west-2c",
    ],
    database_name="mydb",
    master_username="foo",
    master_password="must_be_eight_characters",
    backup_retention_period=5,
    preferred_backup_window="07:00-09:00")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/rds"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := rds.NewCluster(ctx, "postgresql", &rds.ClusterArgs{
			ClusterIdentifier: pulumi.String("aurora-cluster-demo"),
			Engine:            pulumi.String(rds.EngineTypeAuroraPostgresql),
			AvailabilityZones: pulumi.StringArray{
				pulumi.String("us-west-2a"),
				pulumi.String("us-west-2b"),
				pulumi.String("us-west-2c"),
			},
			DatabaseName:          pulumi.String("mydb"),
			MasterUsername:        pulumi.String("foo"),
			MasterPassword:        pulumi.String("must_be_eight_characters"),
			BackupRetentionPeriod: pulumi.Int(5),
			PreferredBackupWindow: pulumi.String("07:00-09:00"),
		})
		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 postgresql = new Aws.Rds.Cluster("postgresql", new()
    {
        ClusterIdentifier = "aurora-cluster-demo",
        Engine = Aws.Rds.EngineType.AuroraPostgresql,
        AvailabilityZones = new[]
        {
            "us-west-2a",
            "us-west-2b",
            "us-west-2c",
        },
        DatabaseName = "mydb",
        MasterUsername = "foo",
        MasterPassword = "must_be_eight_characters",
        BackupRetentionPeriod = 5,
        PreferredBackupWindow = "07:00-09:00",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.rds.Cluster;
import com.pulumi.aws.rds.ClusterArgs;
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 postgresql = new Cluster("postgresql", ClusterArgs.builder()
            .clusterIdentifier("aurora-cluster-demo")
            .engine("aurora-postgresql")
            .availabilityZones(            
                "us-west-2a",
                "us-west-2b",
                "us-west-2c")
            .databaseName("mydb")
            .masterUsername("foo")
            .masterPassword("must_be_eight_characters")
            .backupRetentionPeriod(5)
            .preferredBackupWindow("07:00-09:00")
            .build());

    }
}
resources:
  postgresql:
    type: aws:rds:Cluster
    properties:
      clusterIdentifier: aurora-cluster-demo
      engine: aurora-postgresql
      availabilityZones:
        - us-west-2a
        - us-west-2b
        - us-west-2c
      databaseName: mydb
      masterUsername: foo
      masterPassword: must_be_eight_characters
      backupRetentionPeriod: 5
      preferredBackupWindow: 07:00-09:00

Setting engine to EngineType.AuroraPostgresql provisions an Aurora cluster with PostgreSQL compatibility. The cluster inherits the same availability zone distribution and backup configuration patterns as Aurora MySQL.

Create a Multi-AZ RDS cluster with provisioned IOPS

Multi-AZ RDS clusters provide high availability for MySQL and PostgreSQL with synchronous replication across availability zones. Unlike Aurora, these require explicit storage and compute configuration.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.rds.Cluster("example", {
    clusterIdentifier: "example",
    availabilityZones: [
        "us-west-2a",
        "us-west-2b",
        "us-west-2c",
    ],
    engine: aws.rds.EngineType.Mysql,
    dbClusterInstanceClass: "db.r6gd.xlarge",
    storageType: "io1",
    allocatedStorage: 100,
    iops: 1000,
    masterUsername: "test",
    masterPassword: "mustbeeightcharaters",
});
import pulumi
import pulumi_aws as aws

example = aws.rds.Cluster("example",
    cluster_identifier="example",
    availability_zones=[
        "us-west-2a",
        "us-west-2b",
        "us-west-2c",
    ],
    engine=aws.rds.EngineType.MYSQL,
    db_cluster_instance_class="db.r6gd.xlarge",
    storage_type="io1",
    allocated_storage=100,
    iops=1000,
    master_username="test",
    master_password="mustbeeightcharaters")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/rds"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := rds.NewCluster(ctx, "example", &rds.ClusterArgs{
			ClusterIdentifier: pulumi.String("example"),
			AvailabilityZones: pulumi.StringArray{
				pulumi.String("us-west-2a"),
				pulumi.String("us-west-2b"),
				pulumi.String("us-west-2c"),
			},
			Engine:                 pulumi.String(rds.EngineTypeMysql),
			DbClusterInstanceClass: pulumi.String("db.r6gd.xlarge"),
			StorageType:            pulumi.String("io1"),
			AllocatedStorage:       pulumi.Int(100),
			Iops:                   pulumi.Int(1000),
			MasterUsername:         pulumi.String("test"),
			MasterPassword:         pulumi.String("mustbeeightcharaters"),
		})
		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.Rds.Cluster("example", new()
    {
        ClusterIdentifier = "example",
        AvailabilityZones = new[]
        {
            "us-west-2a",
            "us-west-2b",
            "us-west-2c",
        },
        Engine = Aws.Rds.EngineType.Mysql,
        DbClusterInstanceClass = "db.r6gd.xlarge",
        StorageType = "io1",
        AllocatedStorage = 100,
        Iops = 1000,
        MasterUsername = "test",
        MasterPassword = "mustbeeightcharaters",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.rds.Cluster;
import com.pulumi.aws.rds.ClusterArgs;
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 Cluster("example", ClusterArgs.builder()
            .clusterIdentifier("example")
            .availabilityZones(            
                "us-west-2a",
                "us-west-2b",
                "us-west-2c")
            .engine("mysql")
            .dbClusterInstanceClass("db.r6gd.xlarge")
            .storageType("io1")
            .allocatedStorage(100)
            .iops(1000)
            .masterUsername("test")
            .masterPassword("mustbeeightcharaters")
            .build());

    }
}
resources:
  example:
    type: aws:rds:Cluster
    properties:
      clusterIdentifier: example
      availabilityZones:
        - us-west-2a
        - us-west-2b
        - us-west-2c
      engine: mysql
      dbClusterInstanceClass: db.r6gd.xlarge
      storageType: io1
      allocatedStorage: 100
      iops: 1000
      masterUsername: test
      masterPassword: mustbeeightcharaters

The dbClusterInstanceClass property specifies the compute capacity for each instance in the cluster. The storageType, allocatedStorage, and iops properties configure provisioned IOPS storage. Multi-AZ clusters require all four properties; Aurora clusters manage storage automatically.

Configure Aurora Serverless v2 with auto-scaling

Serverless v2 clusters automatically scale compute capacity based on workload, eliminating the need to provision fixed instance sizes.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.rds.Cluster("example", {
    clusterIdentifier: "example",
    engine: aws.rds.EngineType.AuroraPostgresql,
    engineMode: aws.rds.EngineMode.Provisioned,
    engineVersion: "13.6",
    databaseName: "test",
    masterUsername: "test",
    masterPassword: "must_be_eight_characters",
    storageEncrypted: true,
    serverlessv2ScalingConfiguration: {
        maxCapacity: 1,
        minCapacity: 0,
        secondsUntilAutoPause: 3600,
    },
});
const exampleClusterInstance = new aws.rds.ClusterInstance("example", {
    clusterIdentifier: example.id,
    instanceClass: "db.serverless",
    engine: example.engine.apply((x) => aws.rds.EngineType[x]),
    engineVersion: example.engineVersion,
});
import pulumi
import pulumi_aws as aws

example = aws.rds.Cluster("example",
    cluster_identifier="example",
    engine=aws.rds.EngineType.AURORA_POSTGRESQL,
    engine_mode=aws.rds.EngineMode.PROVISIONED,
    engine_version="13.6",
    database_name="test",
    master_username="test",
    master_password="must_be_eight_characters",
    storage_encrypted=True,
    serverlessv2_scaling_configuration={
        "max_capacity": 1,
        "min_capacity": 0,
        "seconds_until_auto_pause": 3600,
    })
example_cluster_instance = aws.rds.ClusterInstance("example",
    cluster_identifier=example.id,
    instance_class="db.serverless",
    engine=example.engine.apply(lambda x: aws.rds.EngineType(x)),
    engine_version=example.engine_version)
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/rds"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := rds.NewCluster(ctx, "example", &rds.ClusterArgs{
			ClusterIdentifier: pulumi.String("example"),
			Engine:            pulumi.String(rds.EngineTypeAuroraPostgresql),
			EngineMode:        pulumi.String(rds.EngineModeProvisioned),
			EngineVersion:     pulumi.String("13.6"),
			DatabaseName:      pulumi.String("test"),
			MasterUsername:    pulumi.String("test"),
			MasterPassword:    pulumi.String("must_be_eight_characters"),
			StorageEncrypted:  pulumi.Bool(true),
			Serverlessv2ScalingConfiguration: &rds.ClusterServerlessv2ScalingConfigurationArgs{
				MaxCapacity:           pulumi.Float64(1),
				MinCapacity:           pulumi.Float64(0),
				SecondsUntilAutoPause: pulumi.Int(3600),
			},
		})
		if err != nil {
			return err
		}
		_, err = rds.NewClusterInstance(ctx, "example", &rds.ClusterInstanceArgs{
			ClusterIdentifier: example.ID(),
			InstanceClass:     pulumi.String("db.serverless"),
			Engine:            example.Engine.ApplyT(func(x *string) rds.EngineType { return rds.EngineType(*x) }).(rds.EngineTypeOutput),
			EngineVersion:     example.EngineVersion,
		})
		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.Rds.Cluster("example", new()
    {
        ClusterIdentifier = "example",
        Engine = Aws.Rds.EngineType.AuroraPostgresql,
        EngineMode = Aws.Rds.EngineMode.Provisioned,
        EngineVersion = "13.6",
        DatabaseName = "test",
        MasterUsername = "test",
        MasterPassword = "must_be_eight_characters",
        StorageEncrypted = true,
        Serverlessv2ScalingConfiguration = new Aws.Rds.Inputs.ClusterServerlessv2ScalingConfigurationArgs
        {
            MaxCapacity = 1,
            MinCapacity = 0,
            SecondsUntilAutoPause = 3600,
        },
    });

    var exampleClusterInstance = new Aws.Rds.ClusterInstance("example", new()
    {
        ClusterIdentifier = example.Id,
        InstanceClass = "db.serverless",
        Engine = example.Engine.Apply(System.Enum.Parse<Aws.Rds.EngineType>),
        EngineVersion = example.EngineVersion,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.rds.Cluster;
import com.pulumi.aws.rds.ClusterArgs;
import com.pulumi.aws.rds.inputs.ClusterServerlessv2ScalingConfigurationArgs;
import com.pulumi.aws.rds.ClusterInstance;
import com.pulumi.aws.rds.ClusterInstanceArgs;
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 Cluster("example", ClusterArgs.builder()
            .clusterIdentifier("example")
            .engine("aurora-postgresql")
            .engineMode("provisioned")
            .engineVersion("13.6")
            .databaseName("test")
            .masterUsername("test")
            .masterPassword("must_be_eight_characters")
            .storageEncrypted(true)
            .serverlessv2ScalingConfiguration(ClusterServerlessv2ScalingConfigurationArgs.builder()
                .maxCapacity(1.0)
                .minCapacity(0.0)
                .secondsUntilAutoPause(3600)
                .build())
            .build());

        var exampleClusterInstance = new ClusterInstance("exampleClusterInstance", ClusterInstanceArgs.builder()
            .clusterIdentifier(example.id())
            .instanceClass("db.serverless")
            .engine(example.engine())
            .engineVersion(example.engineVersion())
            .build());

    }
}
resources:
  example:
    type: aws:rds:Cluster
    properties:
      clusterIdentifier: example
      engine: aurora-postgresql
      engineMode: provisioned
      engineVersion: '13.6'
      databaseName: test
      masterUsername: test
      masterPassword: must_be_eight_characters
      storageEncrypted: true
      serverlessv2ScalingConfiguration:
        maxCapacity: 1
        minCapacity: 0
        secondsUntilAutoPause: 3600
  exampleClusterInstance:
    type: aws:rds:ClusterInstance
    name: example
    properties:
      clusterIdentifier: ${example.id}
      instanceClass: db.serverless
      engine: ${example.engine}
      engineVersion: ${example.engineVersion}

The engineMode must be set to provisioned (not serverless) for Serverless v2. The serverlessv2ScalingConfiguration block defines capacity boundaries: minCapacity and maxCapacity control the scaling range in Aurora Capacity Units (ACUs). Unlike Serverless v1, you must add a ClusterInstance resource with instanceClass set to “db.serverless”. Note that storageEncrypted defaults to false for provisioned engine mode.

Manage master password with Secrets Manager

Storing database passwords in Secrets Manager removes them from Pulumi state and enables automatic rotation.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const test = new aws.rds.Cluster("test", {
    clusterIdentifier: "example",
    databaseName: "test",
    manageMasterUserPassword: true,
    masterUsername: "test",
});
import pulumi
import pulumi_aws as aws

test = aws.rds.Cluster("test",
    cluster_identifier="example",
    database_name="test",
    manage_master_user_password=True,
    master_username="test")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/rds"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := rds.NewCluster(ctx, "test", &rds.ClusterArgs{
			ClusterIdentifier:        pulumi.String("example"),
			DatabaseName:             pulumi.String("test"),
			ManageMasterUserPassword: pulumi.Bool(true),
			MasterUsername:           pulumi.String("test"),
		})
		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 test = new Aws.Rds.Cluster("test", new()
    {
        ClusterIdentifier = "example",
        DatabaseName = "test",
        ManageMasterUserPassword = true,
        MasterUsername = "test",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.rds.Cluster;
import com.pulumi.aws.rds.ClusterArgs;
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 test = new Cluster("test", ClusterArgs.builder()
            .clusterIdentifier("example")
            .databaseName("test")
            .manageMasterUserPassword(true)
            .masterUsername("test")
            .build());

    }
}
resources:
  test:
    type: aws:rds:Cluster
    properties:
      clusterIdentifier: example
      databaseName: test
      manageMasterUserPassword: true
      masterUsername: test

Setting manageMasterUserPassword to true instructs RDS to create and manage a secret in Secrets Manager. You must omit the masterPassword property when using this feature. RDS handles secret rotation and updates automatically.

Restore a global cluster from snapshot

Global clusters span multiple regions for disaster recovery and low-latency reads. When restoring from a snapshot, you must create the cluster first, then attach it to the global cluster.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = aws.rds.getClusterSnapshot({
    dbClusterIdentifier: "example-original-cluster",
    mostRecent: true,
});
const exampleCluster = new aws.rds.Cluster("example", {
    engine: aws.rds.EngineType.Aurora,
    engineVersion: "5.6.mysql_aurora.1.22.4",
    clusterIdentifier: "example",
    snapshotIdentifier: example.then(example => example.id),
});
const exampleGlobalCluster = new aws.rds.GlobalCluster("example", {
    globalClusterIdentifier: "example",
    sourceDbClusterIdentifier: exampleCluster.arn,
    forceDestroy: true,
});
import pulumi
import pulumi_aws as aws

example = aws.rds.get_cluster_snapshot(db_cluster_identifier="example-original-cluster",
    most_recent=True)
example_cluster = aws.rds.Cluster("example",
    engine=aws.rds.EngineType.AURORA,
    engine_version="5.6.mysql_aurora.1.22.4",
    cluster_identifier="example",
    snapshot_identifier=example.id)
example_global_cluster = aws.rds.GlobalCluster("example",
    global_cluster_identifier="example",
    source_db_cluster_identifier=example_cluster.arn,
    force_destroy=True)
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/rds"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := rds.LookupClusterSnapshot(ctx, &rds.LookupClusterSnapshotArgs{
			DbClusterIdentifier: pulumi.StringRef("example-original-cluster"),
			MostRecent:          pulumi.BoolRef(true),
		}, nil)
		if err != nil {
			return err
		}
		exampleCluster, err := rds.NewCluster(ctx, "example", &rds.ClusterArgs{
			Engine:             pulumi.String(rds.EngineTypeAurora),
			EngineVersion:      pulumi.String("5.6.mysql_aurora.1.22.4"),
			ClusterIdentifier:  pulumi.String("example"),
			SnapshotIdentifier: pulumi.String(example.Id),
		})
		if err != nil {
			return err
		}
		_, err = rds.NewGlobalCluster(ctx, "example", &rds.GlobalClusterArgs{
			GlobalClusterIdentifier:   pulumi.String("example"),
			SourceDbClusterIdentifier: exampleCluster.Arn,
			ForceDestroy:              pulumi.Bool(true),
		})
		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 = Aws.Rds.GetClusterSnapshot.Invoke(new()
    {
        DbClusterIdentifier = "example-original-cluster",
        MostRecent = true,
    });

    var exampleCluster = new Aws.Rds.Cluster("example", new()
    {
        Engine = Aws.Rds.EngineType.Aurora,
        EngineVersion = "5.6.mysql_aurora.1.22.4",
        ClusterIdentifier = "example",
        SnapshotIdentifier = example.Apply(getClusterSnapshotResult => getClusterSnapshotResult.Id),
    });

    var exampleGlobalCluster = new Aws.Rds.GlobalCluster("example", new()
    {
        GlobalClusterIdentifier = "example",
        SourceDbClusterIdentifier = exampleCluster.Arn,
        ForceDestroy = true,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.rds.RdsFunctions;
import com.pulumi.aws.rds.inputs.GetClusterSnapshotArgs;
import com.pulumi.aws.rds.Cluster;
import com.pulumi.aws.rds.ClusterArgs;
import com.pulumi.aws.rds.GlobalCluster;
import com.pulumi.aws.rds.GlobalClusterArgs;
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 example = RdsFunctions.getClusterSnapshot(GetClusterSnapshotArgs.builder()
            .dbClusterIdentifier("example-original-cluster")
            .mostRecent(true)
            .build());

        var exampleCluster = new Cluster("exampleCluster", ClusterArgs.builder()
            .engine("aurora")
            .engineVersion("5.6.mysql_aurora.1.22.4")
            .clusterIdentifier("example")
            .snapshotIdentifier(example.id())
            .build());

        var exampleGlobalCluster = new GlobalCluster("exampleGlobalCluster", GlobalClusterArgs.builder()
            .globalClusterIdentifier("example")
            .sourceDbClusterIdentifier(exampleCluster.arn())
            .forceDestroy(true)
            .build());

    }
}
resources:
  exampleCluster:
    type: aws:rds:Cluster
    name: example
    properties:
      engine: aurora
      engineVersion: 5.6.mysql_aurora.1.22.4
      clusterIdentifier: example
      snapshotIdentifier: ${example.id}
  exampleGlobalCluster:
    type: aws:rds:GlobalCluster
    name: example
    properties:
      globalClusterIdentifier: example
      sourceDbClusterIdentifier: ${exampleCluster.arn}
      forceDestroy: true
variables:
  example:
    fn::invoke:
      function: aws:rds:getClusterSnapshot
      arguments:
        dbClusterIdentifier: example-original-cluster
        mostRecent: true

The getClusterSnapshot function retrieves the most recent snapshot from an existing cluster. The snapshotIdentifier property restores the cluster from that snapshot. The GlobalCluster resource then references the restored cluster’s ARN via sourceDbClusterIdentifier. This two-step process is required; you cannot restore from snapshot and join a global cluster in a single operation.

Beyond these examples

These snippets focus on specific cluster-level features: Aurora and Multi-AZ cluster types, Serverless v2 auto-scaling, Secrets Manager password management, and global cluster snapshot restoration. They’re intentionally minimal rather than full database deployments.

The examples may reference pre-existing infrastructure such as VPC, subnets, and security groups (most examples use defaults), KMS keys for encryption (when not using defaults), and snapshots for restoration scenarios. They focus on configuring the cluster rather than provisioning everything around it.

To keep things focused, common cluster patterns are omitted, including:

  • Storage encryption and KMS key configuration
  • VPC and subnet placement (dbSubnetGroupName)
  • Security group associations (vpcSecurityGroupIds)
  • IAM database authentication and roles
  • Performance Insights and Enhanced Monitoring
  • CloudWatch log exports and backup windows

These omissions are intentional: the goal is to illustrate how each cluster feature is wired, not provide drop-in database modules. See the RDS Cluster resource reference for all available configuration options.

Let's create AWS RDS Aurora Clusters

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Cluster Types & Configuration
What's the difference between Aurora and Multi-AZ DB clusters?
Aurora clusters use Aurora-specific engines (aurora-mysql, aurora-postgresql) and don’t require db_cluster_instance_class. Multi-AZ DB clusters use standard engines (mysql, postgres) and require engine, storage_type, allocated_storage, iops, and db_cluster_instance_class.
How do I create a Serverless v2 cluster?
Set engine_mode to provisioned (not serverless), configure serverlessv2_scaling_configuration, and add an aws.rds.ClusterInstance with instance_class set to db.serverless. Note that storage_encrypted defaults to false for Serverless v2, unlike v1.
Which engine modes are available?
Valid values are global (Aurora MySQL 1.21 and earlier only), parallelquery, provisioned (default), and serverless. Serverless v2 uses provisioned mode, not serverless.
What properties are immutable after cluster creation?
These properties cannot be changed: availability_zones, cluster_identifier, database_name, db_subnet_group_name, db_system_id, engine, engine_mode, kms_key_id, master_username, restore_to_point_in_time, snapshot_identifier, source_region, and storage_encrypted. Multi-AZ clusters also cannot change storage_type.
Security & Password Management
How should I manage the master password securely?
Passwords are stored in plain-text in Pulumi state. For better security, set manage_master_user_password to true to let RDS manage the password in AWS Secrets Manager. You cannot use both manage_master_user_password and master_password together.
Can I use a custom KMS key for Secrets Manager password storage?
Yes, specify master_user_secret_kms_key_id with your KMS key ARN, ID, or alias. If not specified, AWS uses your account’s default KMS key.
How do I enable encryption for my cluster?
Set storage_encrypted to true and optionally specify kms_key_id. Note that encryption defaults differ: false for provisioned mode and true for serverless mode. When restoring from an unencrypted snapshot, you must provide kms_key_id to encrypt the restored cluster.
High Availability & Disaster Recovery
Why does Pulumi show a perpetual diff for availability_zones?
RDS automatically assigns 3 availability zones if you specify fewer than 3, which appears as a diff requiring resource recreation. Specify exactly 3 AZs or use lifecycle.ignore_changes for the availability_zones property.
Can I restore from a snapshot and join a global cluster at the same time?
No, clusters cannot be restored from snapshot and joined to an existing global cluster in a single operation. Restore from the snapshot first, then join the global cluster in a separate step.
What's the default backup retention period?
The default is 1 day. Set backup_retention_period to a higher value for longer retention.
Maintenance & Updates
Will using apply_immediately cause downtime?
Yes, setting apply_immediately to true can result in brief downtime as the server reboots. The default is false, which applies changes during the next maintenance window.
What happens when I update the engine version?
Updating engine_version results in an outage. Plan accordingly and consider using apply_immediately to control when the update occurs.
Can I change the master username after creation?
No, master_username does not support in-place updates and cannot be changed during a restore from snapshot.
Common Configuration Issues
Why am I getting conflicts with IAM role associations?
Don’t use both the standalone aws.rds.ClusterRoleAssociation resource and the iam_roles attribute on the cluster. Use one method or the other to avoid conflicts that overwrite associations.
Why must db_subnet_group_name match across all instances?
The db_subnet_group_name on the cluster must match the db_subnet_group_name specified on every aws.rds.ClusterInstance in the cluster.
Which features are only available for Multi-AZ DB clusters?
Multi-AZ DB clusters (MySQL and PostgreSQL only) support ca_certificate_identifier. Aurora clusters do not support this property.

Using a different cloud?

Explore database guides for other cloud providers: