diff --git a/README.md b/README.md
index 23c769c..cec9a73 100644
--- a/README.md
+++ b/README.md
@@ -58,12 +58,13 @@ $ pulumi up
1. [Project](#project)
2. [Database](#database)
-3. [Redis](#redis)
-4. [StaticSite](#static-site)
-5. [WebServer](#web-server)
-6. [Nuxt SSR](#nuxt-ssr-preset)
-7. [Mongo](#mongo)
-8. [EcsService](#ecs-service)
+3. [Database Replica](#database-replica)
+4. [Redis](#redis)
+5. [StaticSite](#static-site)
+6. [WebServer](#web-server)
+7. [Nuxt SSR](#nuxt-ssr-preset)
+8. [Mongo](#mongo)
+9. [EcsService](#ecs-service)
### Project
@@ -380,6 +381,45 @@ If the password is not specified it will be autogenerated.
The database password is stored as a secret inside AWS Secret Manager.
The secret will be available on the `Database` resource as `password.secret`.
+### Database Replica
+
+AWS RDS Postgres instance.
+
+Features:
+
+- enabled encryption with a symmetric encryption key
+- deployed inside an isolated subnet
+
+
+
+```ts
+new DatabaseReplica(name: string, args: DatabaseReplicaArgs, opts?: pulumi.CustomResourceOptions);
+```
+
+| Argument | Description |
+| :------- | :--------------------------------------------: |
+| name \* | The unique name of the resource. |
+| args \* | The arguments to resource properties. |
+| opts | Bag of options to control resource's behavior. |
+
+```ts
+type DatabaseReplicaArgs = {
+ replicateSourceDb: pulumi.Input;
+ dbSubnetGroupName: pulumi.Input;
+ dbSecurityGroupId: pulumi.Input;
+ monitoringRole?: aws.iam.Role;
+ multiAz?: pulumi.Input;
+ applyImmediately?: pulumi.Input;
+ allocatedStorage?: pulumi.Input;
+ maxAllocatedStorage?: pulumi.Input;
+ instanceClass?: pulumi.Input;
+ tags?: pulumi.Input<{
+ [key: string]: pulumi.Input;
+ }>;
+};
+```
+Database replica requires primary DB instance to exist.
+
### Redis
[Upstash](https://upstash.com) Redis instance.
diff --git a/src/components/database-replica.ts b/src/components/database-replica.ts
new file mode 100644
index 0000000..7fc4327
--- /dev/null
+++ b/src/components/database-replica.ts
@@ -0,0 +1,126 @@
+import * as aws from '@pulumi/aws';
+import * as pulumi from '@pulumi/pulumi';
+import { commonTags } from '../constants';
+
+export type DatabaseReplicaArgs = {
+ /**
+ * ARN of the primary DB that we want to replicate.
+ */
+ replicateSourceDb: pulumi.Input;
+ /**
+ * DB subnet group name. Should be the same as primary instance.
+ * * If primary DB is instance of studion:Database, it can be accessed as
+ * `db.dbSubnetGroup.name`.
+ */
+ dbSubnetGroupName: pulumi.Input;
+ /**
+ * DB security group ID. Should be the same as primary instance.
+ * If primary DB is instance of studion:Database, it can be accessed as
+ * `db.dbSecurityGroup.id`.
+ */
+ dbSecurityGroupId: pulumi.Input;
+ /**
+ * IAM Monitoring role. Should be the same as primary instance.
+ */
+ monitoringRole?: aws.iam.Role;
+ /**
+ * Specifies if the RDS instance is multi-AZ. Defaults to false.
+ */
+ multiAz?: pulumi.Input;
+ /**
+ * Specifies whether any database modifications are applied immediately,
+ * or during the next maintenance window. Default is false.
+ */
+ applyImmediately?: pulumi.Input;
+ /**
+ * The allocated storage in gibibytes. Defaults to 20GB.
+ */
+ allocatedStorage?: pulumi.Input;
+ /**
+ * The upper limit to which Amazon RDS can automatically scale
+ * the storage of the DB instance. Defaults to 100GB.
+ */
+ maxAllocatedStorage?: pulumi.Input;
+ /**
+ * The instance type of the RDS instance. Defaults to 'db.t4g.micro'.
+ */
+ instanceClass?: pulumi.Input;
+ /**
+ * A map of tags to assign to the resource.
+ */
+ tags?: pulumi.Input<{
+ [key: string]: pulumi.Input;
+ }>;
+};
+
+const defaults = {
+ multiAz: false,
+ applyImmediately: false,
+ skipFinalSnapshot: false,
+ allocatedStorage: 20,
+ maxAllocatedStorage: 100,
+ instanceClass: 'db.t4g.micro',
+ enableMonitoring: false,
+};
+
+export class DatabaseReplica extends pulumi.ComponentResource {
+ name: string;
+ instance: aws.rds.Instance;
+ monitoringRole?: aws.iam.Role;
+
+ constructor(
+ name: string,
+ args: DatabaseReplicaArgs,
+ opts: pulumi.ComponentResourceOptions = {},
+ ) {
+ super('studion:DatabaseReplica', name, {}, opts);
+
+ this.name = name;
+
+ const argsWithDefaults = Object.assign({}, defaults, args);
+ this.monitoringRole = argsWithDefaults.monitoringRole;
+ this.instance = this.createDatabaseInstance(args);
+
+ this.registerOutputs();
+ }
+
+ private createDatabaseInstance(args: DatabaseReplicaArgs) {
+ const argsWithDefaults = Object.assign({}, defaults, args);
+ const stack = pulumi.getStack();
+
+ const monitoringOptions =
+ argsWithDefaults.enableMonitoring && this.monitoringRole
+ ? {
+ monitoringInterval: 60,
+ monitoringRoleArn: this.monitoringRole.arn,
+ performanceInsightsEnabled: true,
+ performanceInsightsRetentionPeriod: 7,
+ }
+ : {};
+
+ const instance = new aws.rds.Instance(
+ `${this.name}-rds`,
+ {
+ identifierPrefix: `${this.name}-`,
+ engine: 'postgres',
+ engineVersion: '15.5',
+ allocatedStorage: argsWithDefaults.allocatedStorage,
+ maxAllocatedStorage: argsWithDefaults.maxAllocatedStorage,
+ instanceClass: argsWithDefaults.instanceClass,
+ dbSubnetGroupName: argsWithDefaults.dbSubnetGroupName,
+ vpcSecurityGroupIds: [argsWithDefaults.dbSecurityGroupId],
+ storageEncrypted: true,
+ multiAz: argsWithDefaults.multiAz,
+ publiclyAccessible: false,
+ applyImmediately: argsWithDefaults.applyImmediately,
+ autoMinorVersionUpgrade: true,
+ maintenanceWindow: 'Mon:07:00-Mon:07:30',
+ replicateSourceDb: argsWithDefaults.replicateSourceDb,
+ ...monitoringOptions,
+ tags: { ...commonTags, ...argsWithDefaults.tags },
+ },
+ { parent: this }
+ );
+ return instance;
+ }
+}
diff --git a/src/index.ts b/src/index.ts
index aaeaaec..4bc10ad 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,6 +2,7 @@ export * from './components/web-server';
export * from './components/mongo';
export * from './components/static-site';
export * from './components/database';
+export * from './components/database-replica';
export * from './components/redis';
export * from './components/project';
export * from './components/ec2-ssm-connect';