Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add redis cloud example. #1504

Merged
merged 4 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions redis-cloud-aws-ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/bin/
/node_modules/
Pulumi.*.yaml
3 changes: 3 additions & 0 deletions redis-cloud-aws-ts/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: redis-cloud-aws-ts
desteves marked this conversation as resolved.
Show resolved Hide resolved
runtime: nodejs
description: A minimal TypeScript Pulumi program
57 changes: 57 additions & 0 deletions redis-cloud-aws-ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Redis Enterprise Cloud AWS Example

This example contains code that creates a [Redis Enterprise Cloud](https://app.redislabs.com/) cluster and connects it to an AWS VPC via VPC Peering. This stack also includes an EC2 instance with SSM Session Manager that can run Redis commands to verify connectivity to the Redis cluster.

![Architecture diagram of an AWS VPC across 3 availability zones, connected to Redis Enterprise Cloud via VPC peering](images/architecture.png)

## Prerequisites

1. [Install Pulumi](https://www.pulumi.com/docs/get-started/install/)
1. [Configure the Redis Cloud provider](https://www.pulumi.com/registry/packages/rediscloud/installation-configuration/)
1. Ensure you have a payment method specified in the Redis Enterprise Cloud console.
1. [Configure the AWS Classic provider](https://www.pulumi.com/registry/packages/aws/installation-configuration/)
1. [Install NodeJS](https://nodejs.org/en/download)

## Deploy the app

1. Set configuration values for your Redis Cloud payment information:

```bash
pulumi config set cardType <YOUR-CARD-TYPE> # e.g. Visa (capitalization matters)
pulumi config set lastFourNumbers 1234
jkodroff marked this conversation as resolved.
Show resolved Hide resolved
```

1. If you are using an availability zone other than `us-east-1`, you will need to set `preferredAz`, e.g. if you're using `us-west-2`:
desteves marked this conversation as resolved.
Show resolved Hide resolved

```bash
pulumi config set aws:region us-west-2 # Not necessary if your default AWS region is e.g. us-west-2
pulumi config set preferredAz usw2-az1 # This step is necessary is your default or selected region is not us-east-1
```

1. Deploy the architecture:

```bash
pulumi up
desteves marked this conversation as resolved.
Show resolved Hide resolved
```

## Test the architecture

1. In the AWS Console, find the EC2 instance named `pulumi-redis-cloud-tester`, and connect to it via SSM Systems Manager:

![AWS EC2 console, listing available instances](images/ec2-console.png)
![AWS EC2 connect dialog](images/ec2-connect.png)

1. In the [Redis Cloud console](https://app.redislabs.com/), locate your database, and select "Connect":

1. In the connection dialog, copy the connection info for the Redis CLI:

![Redis Cloud console showing the connection dialog with the CLI option selected](images/redis-console.png)

1. Paste the command into the Systems Manager shell prompt, which will connect you to the Redis instance. From there you can run Redis commands like `INCR` that will write to the Redis cluster:

![SSM Systems Manager shell with commands like redis connect and INCR](images/shell-commands.png)

## Clean up

1. Run `pulumi destroy` to tear down all resources.
1. To delete the stack itself, run `pulumi stack rm`. Note that this command deletes all deployment history from the Pulumi Cloud console.
Binary file added redis-cloud-aws-ts/images/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added redis-cloud-aws-ts/images/ec2-connect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added redis-cloud-aws-ts/images/ec2-console.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added redis-cloud-aws-ts/images/redis-console-db.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added redis-cloud-aws-ts/images/redis-console.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added redis-cloud-aws-ts/images/shell-commands.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
164 changes: 164 additions & 0 deletions redis-cloud-aws-ts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright 2016-2023, Pulumi Corporation. All rights reserved.
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
import * as pulumi from "@pulumi/pulumi";
import * as rediscloud from "@rediscloud/pulumi-rediscloud";

const config = new pulumi.Config();

const region = aws.config.region ?? "";

const getPreferredAz = function () {
const value = config.get("preferredAz");

if (region.toLowerCase() !== "us-east-1" && !value) {
throw new Error("preferredAz must be defined if AWS region is not us-east-1.");
}

return value ?? "use1-az1";
};

const preferredAvailabilityZone = getPreferredAz();
const redisVpcCidr = "10.0.0.0/24";

// NOTE: cardType is case-sensitive: "Visa" will work. "visa" will not.
const card = rediscloud.getPaymentMethodOutput({
cardType: config.require("cardType"),
lastFourNumbers: config.require("lastFourNumbers"),
});

const subscription = new rediscloud.Subscription("redis-subscription", {
name: "pulumi-redis-aws-example",
paymentMethod: "credit-card",
paymentMethodId: card.id,
cloudProvider: {
regions: [
{
region: region,
networkingDeploymentCidr: redisVpcCidr,
// We use a single AZ here to minimize the cost incurred from running
// this example:
multipleAvailabilityZones: false,
preferredAvailabilityZones: [preferredAvailabilityZone],
},
],
},

creationPlan: {
memoryLimitInGb: 10,
quantity: 1,
replication: true,
supportOssClusterApi: false,
throughputMeasurementBy: "operations-per-second",
throughputMeasurementValue: 20000,
modules: ["RedisJSON"],
},
});

const database = new rediscloud.SubscriptionDatabase("redis-db", {
name: "my-db",
subscriptionId: subscription.id,
protocol: "redis",
memoryLimitInGb: 10,
dataPersistence: "aof-every-1-second",
throughputMeasurementBy: "operations-per-second",
throughputMeasurementValue: 20000,
replication: true,
});

export const privateEndpoint = database.privateEndpoint;
export const publicEndpoint = database.publicEndpoint;

const vpc = new awsx.ec2.Vpc("vpc", {
cidrBlock: "10.1.0.0/16", // Cannot conflict with the Redis CIDR block,
natGateways: {
strategy: "Single",
},
});

const callerIdentity = aws.getCallerIdentity();

const peering = new rediscloud.SubscriptionPeering("redis-peering", {
subscriptionId: subscription.id,
region: region,
awsAccountId: callerIdentity.then(x => x.accountId),
vpcId: vpc.vpcId,
vpcCidr: vpc.vpc.cidrBlock,
});

/* tslint:disable:no-unused-expression */
new aws.ec2.VpcPeeringConnectionAccepter("aws-peering-accepter", {
vpcPeeringConnectionId: peering.awsPeeringId,
autoAccept: true,
});

const sg = new aws.ec2.SecurityGroup("instance-sg", {
description: "Allow all egress traffic.",
vpcId: vpc.vpcId,
egress: [{
cidrBlocks: ["0.0.0.0/0"],
description: "Allow all",
protocol: "-1",
fromPort: 0,
toPort: 0,
}],
});

const instanceRole = new aws.iam.Role("instance-role", {
assumeRolePolicy: JSON.stringify({
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com",
},
"Action": "sts:AssumeRole",
},
}),
});

new aws.iam.RolePolicyAttachment("instance-role-attachment", {
role: instanceRole.name,
policyArn: "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
});

const instanceProfile = new aws.iam.InstanceProfile("instance-profile", {
role: instanceRole.name,
});

const amazonLinux2 = aws.ec2.getAmiOutput({
mostRecent: true,
owners: ["amazon"],
filters: [
{ name: "name", values: ["amzn2-ami-hvm-*-x86_64-gp2"] },
{ name: "owner-alias", values: ["amazon"] },
],
});

new aws.ec2.Instance("instance", {
ami: amazonLinux2.id,
instanceType: "t3.micro",
vpcSecurityGroupIds: [sg.id],
subnetId: vpc.privateSubnetIds[0],
tags: {
Name: "pulumi-redis-cloud-tester",
},
iamInstanceProfile: instanceProfile.name,
userData: `#!/bin/bash
sudo amazon-linux-extras install redis6
`,
});

vpc.privateSubnetIds.apply(ids => {
ids.forEach((id, index) => {
const routeTable = aws.ec2.getRouteTableOutput({
subnetId: id,
});

new aws.ec2.Route(`peering-route-${index}`, {
routeTableId: routeTable.id,
destinationCidrBlock: redisVpcCidr,
vpcPeeringConnectionId: peering.awsPeeringId,
});
});
});
13 changes: 13 additions & 0 deletions redis-cloud-aws-ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "redis-cloud-aws-ts",
"main": "index.ts",
"devDependencies": {
"@types/node": "^16"
},
"dependencies": {
"@pulumi/aws": "^6.2.1",
"@pulumi/awsx": "^1.0.5",
"@pulumi/pulumi": "^3.0.0",
"@rediscloud/pulumi-rediscloud": "^1.2.6"
}
}
18 changes: 18 additions & 0 deletions redis-cloud-aws-ts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2016",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.ts"
]
}
Loading